diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000000..4bfc5ddd8a --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,15 @@ +name: Test build and package QubesOS RPMs + +on: + push: + branches: + - 'aem*' + tags: + - '*' + +jobs: + qubes-dom0-package: + uses: TrenchBoot/.github/.github/workflows/qubes-dom0-packagev2.yml@master + with: + qubes-component: 'vmm-xen' + qubes-pkg-src-dir: '.' diff --git a/.github/workflows/coverity.yml b/.github/workflows/coverity.yml deleted file mode 100644 index a6c2819b0a..0000000000 --- a/.github/workflows/coverity.yml +++ /dev/null @@ -1,55 +0,0 @@ -name: Coverity Scan - -# We only want to test official release code, not every pull request. -on: - workflow_dispatch: - schedule: - - cron: '18 9 * * WED,SUN' # Bi-weekly at 9:18 UTC - -jobs: - coverity: - runs-on: ubuntu-24.04 - steps: - - name: Install build dependencies - run: | - sudo apt-get update - sudo apt-get install -y \ - build-essential \ - git-core \ - golang \ - iasl \ - libbz2-dev \ - libext2fs-dev \ - liblzma-dev \ - libncurses5-dev \ - libyajl-dev \ - libzstd-dev \ - ocaml \ - ocaml-findlib \ - python3-dev \ - uuid-dev \ - zlib1g-dev \ - - - uses: actions/checkout@v4 - with: - ref: staging - - - name: Configure Xen - run: | - ./configure \ - --disable-docs \ - --disable-stubdom \ - --with-system-qemu=/bin/true \ - --with-system-seabios=/bin/true \ - --with-system-ovmf=/bin/true \ - - - name: Pre build stuff - run: | - make -j`nproc` mini-os-dir - - - uses: vapier/coverity-scan-action@v1 - with: - command: make -j`nproc` build-xen build-tools && make -j`nproc` -C extras/mini-os/ - project: XenProject - email: ${{ secrets.COVERITY_SCAN_EMAIL }} - token: ${{ secrets.COVERITY_SCAN_TOKEN }} diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index ef4484e09a..5a9b8b7228 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,3 +1,6 @@ +variables: + XEN_REGISTRY: registry.gitlab.com/xen-project/xen + workflow: rules: - if: $CI_COMMIT_BRANCH =~ /^(master|smoke|^coverity-tested\/.*|stable-.*)$/ @@ -5,11 +8,21 @@ workflow: - when: always stages: + - containers - analyze - build - test include: - - 'automation/gitlab-ci/analyze.yaml' - - 'automation/gitlab-ci/build.yaml' - - 'automation/gitlab-ci/test.yaml' + - local: 'automation/gitlab-ci/containers.yaml' + rules: + - if: $XEN_CI_REBUILD_CONTAINERS + - local: 'automation/gitlab-ci/analyze.yaml' + rules: + - if: $XEN_CI_REBUILD_CONTAINERS == null + - local: 'automation/gitlab-ci/build.yaml' + rules: + - if: $XEN_CI_REBUILD_CONTAINERS == null + - local: 'automation/gitlab-ci/test.yaml' + rules: + - if: $XEN_CI_REBUILD_CONTAINERS == null diff --git a/automation/eclair_analysis/ECLAIR/tagging.ecl b/automation/eclair_analysis/ECLAIR/tagging.ecl index 9318e5b10c..7944ce2ee3 100644 --- a/automation/eclair_analysis/ECLAIR/tagging.ecl +++ b/automation/eclair_analysis/ECLAIR/tagging.ecl @@ -115,7 +115,7 @@ if(string_equal(target,"x86_64"), ) if(string_equal(target,"arm64"), - service_selector({"additional_clean_guidelines","MC3R1.R2.1||MC3R1.R5.3||MC3.R11.2||MC3R1.R16.6||MC3R1.R20.7"}) + service_selector({"additional_clean_guidelines","MC3R1.R2.1||MC3R1.R5.3||MC3R1.R8.4||MC3.R11.2||MC3R1.R16.6||MC3R1.R20.7"}) ) -reports+={clean:added,"service(clean_guidelines_common||additional_clean_guidelines)"} diff --git a/automation/gitlab-ci/build.yaml b/automation/gitlab-ci/build.yaml index ce39b0ee21..1ca6764225 100644 --- a/automation/gitlab-ci/build.yaml +++ b/automation/gitlab-ci/build.yaml @@ -1,6 +1,6 @@ .build-tmpl: &build stage: build - image: registry.gitlab.com/xen-project/xen/${CONTAINER} + image: ${XEN_REGISTRY}/${CONTAINER} script: - ./automation/scripts/build 2>&1 | tee build.log artifacts: @@ -208,7 +208,7 @@ .yocto-test: stage: build - image: registry.gitlab.com/xen-project/xen/${CONTAINER} + image: ${XEN_REGISTRY}/${CONTAINER} script: - ./automation/build/yocto/build-yocto.sh -v --log-dir=./logs --xen-dir=`pwd` ${YOCTO_BOARD} ${YOCTO_OUTPUT} variables: diff --git a/automation/gitlab-ci/containers.yaml b/automation/gitlab-ci/containers.yaml new file mode 100644 index 0000000000..25e8bdc34b --- /dev/null +++ b/automation/gitlab-ci/containers.yaml @@ -0,0 +1,29 @@ +.container-build-tmpl: + stage: containers + image: docker:stable + tags: + - container-builder + rules: + - if: $XEN_CI_REBUILD_CONTAINERS + services: + - docker:dind + before_script: + - apk add make + - docker info + - docker login -u $CI_DEPLOY_USER -p $CI_DEPLOY_PASSWORD $CI_REGISTRY + script: + - make -C automation/build PUSH=1 REGISTRY=${XEN_REGISTRY} ${CONTAINER/:/\/} + after_script: + - docker logout + +container-archlinux-current: + extends: + - .container-build-tmpl + variables: + CONTAINER: "archlinux:current" + +container-opensuse-tumbleweed-x86_64: + extends: + - .container-build-tmpl + variables: + CONTAINER: "opensuse:tumbleweed-x86_64" diff --git a/automation/gitlab-ci/test.yaml b/automation/gitlab-ci/test.yaml index f5dd4de757..1822e3ea5f 100644 --- a/automation/gitlab-ci/test.yaml +++ b/automation/gitlab-ci/test.yaml @@ -1,6 +1,6 @@ .test-jobs-common: stage: test - image: registry.gitlab.com/xen-project/xen/${CONTAINER} + image: ${XEN_REGISTRY}/${CONTAINER} .arm64-test-needs: &arm64-test-needs - alpine-3.18-arm64-rootfs-export @@ -98,9 +98,8 @@ - '*.log' - '*.dtb' when: always - only: - variables: - - $XILINX_JOBS == "true" && $CI_COMMIT_REF_PROTECTED == "true" + rules: + - if: $XILINX_JOBS == "true" && $CI_COMMIT_REF_PROTECTED == "true" tags: - xilinx @@ -117,9 +116,8 @@ - smoke.serial - '*.log' when: always - only: - variables: - - $XILINX_JOBS == "true" && $CI_COMMIT_REF_PROTECTED == "true" + rules: + - if: $XILINX_JOBS == "true" && $CI_COMMIT_REF_PROTECTED == "true" tags: - xilinx @@ -137,9 +135,8 @@ - smoke.serial - '*.log' when: always - only: - variables: - - $QUBES_JOBS == "true" && $CI_COMMIT_REF_PROTECTED == "true" + rules: + - if: $QUBES_JOBS == "true" && $CI_COMMIT_REF_PROTECTED == "true" tags: - qubes-hw2 diff --git a/docs/hypervisor-guide/x86/how-xen-boots.rst b/docs/hypervisor-guide/x86/how-xen-boots.rst index 8b3229005c..98c196516b 100644 --- a/docs/hypervisor-guide/x86/how-xen-boots.rst +++ b/docs/hypervisor-guide/x86/how-xen-boots.rst @@ -55,6 +55,11 @@ If ``CONFIG_PVH_GUEST`` was selected at build time, an Elf note is included which indicates the ability to use the PVH boot protocol, and registers ``__pvh_start`` as the entrypoint, entered in 32bit mode. +MLE header is used with Intel TXT, together with MB2 headers. Entrypoint is +different, but it is used just to differentiate from other entries by moving +a magic number to EAX. Execution environment is similar to that of Multiboot 2 +and code falls through to ``start``. + xen.gz ~~~~~~ diff --git a/docs/misra/rules.rst b/docs/misra/rules.rst index 4a144da8d6..e7763795b8 100644 --- a/docs/misra/rules.rst +++ b/docs/misra/rules.rst @@ -154,7 +154,7 @@ maintainers if you want to suggest a change. * - `Rule 5.1 `_ - Required - External identifiers shall be distinct - - The Xen characters limit for identifiers is 40. Public headers + - The Xen characters limit for identifiers is 63. Public headers (xen/include/public/) are allowed to retain longer identifiers for backward compatibility. @@ -162,7 +162,7 @@ maintainers if you want to suggest a change. - Required - Identifiers declared in the same scope and name space shall be distinct - - The Xen characters limit for identifiers is 40. Public headers + - The Xen characters limit for identifiers is 63. Public headers (xen/include/public/) are allowed to retain longer identifiers for backward compatibility. @@ -177,7 +177,7 @@ maintainers if you want to suggest a change. * - `Rule 5.4 `_ - Required - Macro identifiers shall be distinct - - The Xen characters limit for macro identifiers is 40. Public + - The Xen characters limit for macro identifiers is 63. Public headers (xen/include/public/) are allowed to retain longer identifiers for backward compatibility. diff --git a/qubesos_xen.logrotate b/qubesos_xen.logrotate new file mode 100644 index 0000000000..6bf2ae091a --- /dev/null +++ b/qubesos_xen.logrotate @@ -0,0 +1,9 @@ +/var/log/xen/xen-hotplug.log +/var/log/xen/domain-builder-ng.log +/var/log/xen/console/*.log{ + notifempty + missingok + compress + copytruncate + su root qubes +} diff --git a/qubesos_xen.modules-load.conf b/qubesos_xen.modules-load.conf new file mode 100644 index 0000000000..2585265bff --- /dev/null +++ b/qubesos_xen.modules-load.conf @@ -0,0 +1,9 @@ +xen-evtchn +xen-gntdev +xen-gntalloc +xen-blkback +xen-pciback +xen-privcmd +xen-acpi-processor +# Not used in Qubes dom0 +#xen-netback diff --git a/qubesos_xen_config b/qubesos_xen_config new file mode 100644 index 0000000000..4bebb459df --- /dev/null +++ b/qubesos_xen_config @@ -0,0 +1,154 @@ +# +# Automatically generated file; DO NOT EDIT. +# Xen/x86 4.17.3 Configuration +# +CONFIG_CC_IS_GCC=y +CONFIG_GCC_VERSION=120301 +CONFIG_CLANG_VERSION=0 +CONFIG_LD_IS_GNU=y +CONFIG_CC_HAS_VISIBILITY_ATTRIBUTE=y +CONFIG_X86_64=y +CONFIG_X86=y +CONFIG_ARCH_DEFCONFIG="arch/x86/configs/x86_64_defconfig" +CONFIG_CC_HAS_INDIRECT_THUNK=y +CONFIG_HAS_AS_CET_SS=y +CONFIG_HAS_CC_CET_IBT=y + +# +# Architecture Features +# +CONFIG_64BIT=y +CONFIG_NR_CPUS=256 +CONFIG_PV=y +# CONFIG_PV32 is not set +# CONFIG_PV_LINEAR_PT is not set +CONFIG_HVM=y +CONFIG_XEN_SHSTK=y +CONFIG_XEN_IBT=y +# CONFIG_SHADOW_PAGING is not set +# CONFIG_BIGMEM is not set +# CONFIG_HVM_FEP is not set +CONFIG_TBOOT=y +CONFIG_XEN_ALIGN_DEFAULT=y +# CONFIG_XEN_ALIGN_2M is not set +# CONFIG_X2APIC_PHYSICAL is not set +# CONFIG_X2APIC_CLUSTER is not set +CONFIG_X2APIC_MIXED=y +# CONFIG_XEN_GUEST is not set +# CONFIG_HYPERV_GUEST is not set +# CONFIG_MEM_PAGING is not set +# CONFIG_MEM_SHARING is not set +# end of Architecture Features + +# +# Common Features +# +CONFIG_COMPAT=y +CONFIG_CORE_PARKING=y +CONFIG_GRANT_TABLE=y +CONFIG_ALTERNATIVE_CALL=y +CONFIG_ARCH_MAP_DOMAIN_PAGE=y +CONFIG_HAS_ALTERNATIVE=y +CONFIG_HAS_COMPAT=y +CONFIG_HAS_DIT=y +CONFIG_HAS_EX_TABLE=y +CONFIG_HAS_FAST_MULTIPLY=y +CONFIG_HAS_IOPORTS=y +CONFIG_HAS_KEXEC=y +CONFIG_HAS_PDX=y +CONFIG_HAS_SCHED_GRANULARITY=y +CONFIG_HAS_UBSAN=y +CONFIG_MEM_ACCESS_ALWAYS_ON=y +CONFIG_MEM_ACCESS=y +CONFIG_NEEDS_LIBELF=y +CONFIG_NUMA=y + +# +# Speculative hardening +# +CONFIG_INDIRECT_THUNK=y +CONFIG_SPECULATIVE_HARDEN_ARRAY=y +CONFIG_SPECULATIVE_HARDEN_BRANCH=y +CONFIG_SPECULATIVE_HARDEN_GUEST_ACCESS=y +CONFIG_SPECULATIVE_HARDEN_LOCK=y +# end of Speculative hardening + +CONFIG_DIT_DEFAULT=y +CONFIG_HYPFS=y +CONFIG_HYPFS_CONFIG=y +CONFIG_IOREQ_SERVER=y +# CONFIG_KEXEC is not set +CONFIG_EFI_SET_VIRTUAL_ADDRESS_MAP=y +CONFIG_XENOPROF=y +# CONFIG_XSM is not set +# CONFIG_ARGO is not set + +# +# Schedulers +# +CONFIG_SCHED_CREDIT=y +CONFIG_SCHED_CREDIT2=y +CONFIG_SCHED_RTDS=y +# CONFIG_SCHED_ARINC653 is not set +CONFIG_SCHED_NULL=y +# CONFIG_SCHED_CREDIT_DEFAULT is not set +CONFIG_SCHED_CREDIT2_DEFAULT=y +# CONFIG_SCHED_RTDS_DEFAULT is not set +# CONFIG_SCHED_NULL_DEFAULT is not set +CONFIG_SCHED_DEFAULT="credit2" +# end of Schedulers + +CONFIG_CRYPTO=y +# CONFIG_LIVEPATCH is not set +# CONFIG_ENFORCE_UNIQUE_SYMBOLS is not set +CONFIG_SUPPRESS_DUPLICATE_SYMBOL_WARNINGS=y +CONFIG_CMDLINE="ept=exec-sp spec-ctrl=unpriv-mmio" +# CONFIG_CMDLINE_OVERRIDE is not set +CONFIG_DOM0_MEM="min:1024M,max:4096M" +# CONFIG_TRACEBUFFER is not set +# end of Common Features + +# +# Device Drivers +# +CONFIG_ACPI=y +CONFIG_ACPI_LEGACY_TABLES_LOOKUP=y +CONFIG_ACPI_NUMA=y +CONFIG_HAS_NS16550=y +CONFIG_HAS_EHCI=y +CONFIG_SERIAL_TX_BUFSIZE=16384 +CONFIG_XHCI=y +CONFIG_HAS_CPUFREQ=y +CONFIG_HAS_PASSTHROUGH=y +# CONFIG_IOMMU_QUARANTINE_NONE is not set +CONFIG_IOMMU_QUARANTINE_BASIC=y +# CONFIG_IOMMU_QUARANTINE_SCRATCH_PAGE is not set +CONFIG_HAS_PCI=y +CONFIG_HAS_PCI_MSI=y +CONFIG_VIDEO=y +CONFIG_VGA=y +CONFIG_HAS_VPCI=y +# end of Device Drivers + +CONFIG_EXPERT=y +CONFIG_UNSUPPORTED=y +CONFIG_ARCH_SUPPORTS_INT128=y + +# +# Debugging Options +# +# CONFIG_DEBUG is not set +# CONFIG_CRASH_DEBUG is not set +CONFIG_GDBSX=y +CONFIG_DEBUG_INFO=y +# CONFIG_FRAME_POINTER is not set +# CONFIG_COVERAGE is not set +# CONFIG_DEBUG_LOCK_PROFILE is not set +# CONFIG_DEBUG_LOCKS is not set +# CONFIG_PERF_COUNTERS is not set +# CONFIG_VERBOSE_DEBUG is not set +CONFIG_SCRUB_DEBUG=y +# CONFIG_UBSAN is not set +# CONFIG_DEBUG_TRACE is not set +# CONFIG_XMEM_POOL_POISON is not set +# end of Debugging Options diff --git a/vmm-xen.spec.in b/vmm-xen.spec.in new file mode 100644 index 0000000000..efce33c80e --- /dev/null +++ b/vmm-xen.spec.in @@ -0,0 +1,952 @@ +# Build ocaml bits unless rpmbuild was run with --without ocaml +# or ocamlopt is missing (the xen makefile doesn't build ocaml bits if it isn't there) +%define with_ocaml 0 +%define build_ocaml 0 +# Build with docs unless rpmbuild was run with --without docs +%define build_docs %{?_without_docs: 0} %{?!_without_docs: 1} +# Build without stubdom unless rpmbuild was run with --with stubdom +%define build_stubdom %{?_with_stubdom: 1} %{?!_with_stubdom: 0} +# Build without qemu-traditional unless rpmbuild was run with --with qemutrad +%define build_qemutrad %{?_with_qemutrad: 1} %{?!_with_qemutrad: 0} +# build with ovmf from edk2-ovmf unless rpmbuild was run with --without ovmf +%define build_ovmf %{?_without_ovmf: 0} %{?!_without_ovmf: 1} +# set to 0 for archs that don't use qemu or ovmf (reduces build dependencies) +%ifnarch x86_64 %{ix86} +%define build_qemutrad 0 +%define build_ovmf 0 +%endif +%if ! %build_qemutrad +%define build_stubdom 0 +%endif +# Build with xen hypervisor unless rpmbuild was run with --without hyp +%define build_hyp %{?_without_hyp: 0} %{?!_without_hyp: 1} +# build xsm support unless rpmbuild was run with --without xsm +# or required packages are missing +%define with_xsm 0 +%define build_xsm 0 +# cross compile 64-bit hypervisor on ix86 unless rpmbuild was run +# with --without crosshyp +%define build_crosshyp %{?_without_crosshyp: 0} %{?!_without_crosshyp: 1} +%ifnarch %{ix86} +%define build_crosshyp 0 +%else +%if ! %build_crosshyp +%define build_hyp 0 +%endif +%endif +# no point in trying to build xsm on ix86 without a hypervisor +%if ! %build_hyp +%define build_xsm 0 +%endif +# build an efi boot image (where supported) unless rpmbuild was run with +# --without efi +%define build_efi 1 +# xen only supports efi boot images on x86_64 or aarch64 +# i686 builds a x86_64 hypervisor so add that as well +%ifnarch x86_64 aarch64 %{ix86} +%define build_efi 0 +%endif +%if "%dist" >= ".fc20" +%define with_systemd_presets 1 +%else +%define with_systemd_presets 0 +%endif + +# workaround for https://bugzilla.redhat.com/1671883 (dwz leaving temp files of +# hardlinked sources) +%define _unpackaged_files_terminate_build 0 + +# xen.efi.elf doesn't have proper build-id +%define _missing_build_ids_terminate_build 0 + +# Hypervisor ABI +%define hv_abi 4.19 + +%define upstream_version @VERSION@ +%define rctag %(echo @VERSION@ | sed -n -e 's/.*-\\(rc[0-9]*\\).*/0.\\1./;/rc/p') + +Summary: Xen is a virtual machine monitor +Name: xen +Version: %(echo @VERSION@ | sed 's/-rc.*//') +Release: %{?rctag}@REL@%{?dist} +Epoch: 2001 +License: GPLv2+ and LGPLv2+ and BSD +URL: http://xen.org/ +Source0: %{name}-%{version}.tar.gz +Source4: qubesos_xen_config +Source2: qubesos_xen.logrotate +Source3: qubesos_xen.modules-load.conf +Provides: xen-gvt + +# This fixes building .efi file. While it isn't used (even on UEFI) in the final +# system, keep it to create RPMs similar to the original ones. +# TODO: these are included as commits right now for easier conflict resolution, +# but ultimately they should be part of Qubes patches, not upstream Xen +#Patch0203: qubesos_0203-xen.efi.build.patch +#Patch1019: qubesos_1019-x86-boot-Makefile-align-text_diff-to-0x40-bytes.patch + +%if %build_qemutrad +BuildRequires: libidn-devel zlib-devel SDL-devel curl-devel +BuildRequires: libX11-devel gtk2-devel libaio-devel +%endif +# build using Fedora seabios and ipxe packages for roms +BuildRequires: seabios-bin ipxe-roms-qemu +%ifarch %{ix86} x86_64 +# for the VMX "bios" +BuildRequires: dev86 +%endif +BuildRequires: python%{python3_pkgversion}-devel ncurses-devel python%{python3_pkgversion}-setuptools +BuildRequires: perl-interpreter perl-generators +# BEGIN QUBES SPECIFIC PART +BuildRequires: autoconf +BuildRequires: automake +# END QUBES SPECIFIC PART +BuildRequires: gettext +BuildRequires: zlib-devel +# Several tools now use uuid +BuildRequires: libuuid-devel +# iasl needed to build hvmloader +BuildRequires: acpica-tools +# modern compressed kernels +BuildRequires: bzip2-devel xz-devel libzstd-devel +# BEGIN QUBES SPECIFIC PART +## libfsimage +#BuildRequires: e2fsprogs-devel +# tools now require yajl and wget +BuildRequires: yajl-devel +# END QUBES SPECIFIC PART +# remus support now needs libnl3 +BuildRequires: libnl3-devel +%if %with_xsm +# xsm policy file needs needs checkpolicy and m4 +BuildRequires: checkpolicy m4 +%endif +%if %build_crosshyp +# cross compiler for building 64-bit hypervisor on ix86 +BuildRequires: gcc-x86_64-linux-gnu +%endif +BuildRequires: gcc make +Requires: iproute +Requires: python%{python3_pkgversion}-lxml +Requires: xen-runtime = %{epoch}:%{version}-%{release} +# Not strictly a dependency, but kpartx is by far the most useful tool right +# now for accessing domU data from within a dom0 so bring it in when the user +# installs xen. +Requires: kpartx +ExclusiveArch: x86_64 aarch64 +#ExclusiveArch: %#{ix86} x86_64 ia64 noarch +%if %with_ocaml +BuildRequires: ocaml, ocaml-findlib +BuildRequires: perl(Data::Dumper) +%endif +%if %with_systemd_presets +Requires(post): systemd +Requires(preun): systemd +BuildRequires: systemd +%endif +BuildRequires: systemd-devel +%ifarch armv7hl aarch64 +BuildRequires: libfdt-devel +%endif +%if %build_ovmf +BuildRequires: edk2-ovmf +%endif +%if %build_hyp +BuildRequires: bison flex +%endif +BuildRequires: rsync + +%description +This package contains the XenD daemon and xm command line +tools, needed to manage virtual machines running under the +Xen hypervisor + +# BEGIN QUBES SPECIFIC PART +%package -n python%{python3_pkgversion}-%{name} +Summary: python%{python3_pkgversion} bindings for Xen tools +Group: Development/Libraries +Requires: xen-libs = %{epoch}:%{version}-%{release} +Requires: python%{python3_pkgversion} +%{?python_provide:%python_provide python%{python3_pkgversion}-%{name}} + +%description -n python%{python3_pkgversion}-%{name} +This package contains python%{python3_pkgversion} bindings to Xen tools. Especially xen.lowlevel.xs +and xen.lowlevel.xc modules. +# END QUBES SPECIFIC PART + +%package libs +Summary: Libraries for Xen tools +Requires: xen-licenses +# BEGIN QUBES SPECIFIC PART +Provides: xen-gvt-libs +# toolstack <-> stubdomain API change +Conflicts: xen-hvm-stubdom-linux < 1.2.5 +Conflicts: xen-hvm-stubdom-linux-full < 1.2.5 +# libxl ABI change +Conflicts: libvirt-daemon-driver-libxl < 1000:6.6.0-7 +# END QUBES SPECIFIC PART + +%description libs +This package contains the libraries needed to run applications +which manage Xen virtual machines. + + +%package runtime +Summary: Core Xen runtime environment +Requires: xen-libs = %{epoch}:%{version}-%{release} +#Requires: /usr/bin/qemu-img /usr/bin/qemu-nbd +Requires: /usr/bin/qemu-img +# Ensure we at least have a suitable kernel installed, though we can't +# force user to actually boot it. +Requires: xen-hypervisor-abi = %{hv_abi} +# BEGIN QUBES SPECIFIC PART +# perl is used in /etc/xen/scripts/locking.sh +# Recommends: perl +%ifnarch armv7hl aarch64 +# use /usr/bin/qemu-system-i386 in Fedora instead of qemu-xen +#Recommends: qemu-system-x86-core +# rom file for qemu-xen-traditional +Recommends: ipxe-roms-qemu +%endif +Requires: seabios-bin +# END QUBES SPECIFIC PART + +%description runtime +This package contains the runtime programs and daemons which +form the core Xen userspace environment. + + +%package hypervisor +Summary: Libraries for Xen tools +Provides: xen-hypervisor-abi = %{hv_abi} +Requires: xen-licenses +%if %build_hyp +%ifarch %{ix86} +Recommends: grub2-pc-modules +%endif +%ifarch x86_64 +# BEGIN QUBES SPECIFIC PART +#Recommends: grub2-pc-modules grub2-efi-x64-modules +# END QUBES SPECIFIC PART +%endif +%endif + +%description hypervisor +This package contains the Xen hypervisor + + +%if %build_docs +%package doc +Summary: Xen documentation +BuildArch: noarch +Requires: xen-licenses +# for the docs +BuildRequires: perl(Pod::Man) perl(Pod::Text) perl(File::Find) +BuildRequires: transfig pandoc perl(Pod::Html) + +%description doc +This package contains the Xen documentation. +%endif + + +%package devel +Summary: Development libraries for Xen tools +Requires: xen-libs = %{epoch}:%{version}-%{release} +Requires: libuuid-devel + +%description devel +This package contains what's needed to develop applications +which manage Xen virtual machines. + + +%package licenses +Summary: License files from Xen source + +%description licenses +This package contains the license files from the source used +to build the xen packages. + + +%if %build_ocaml +%package ocaml +Summary: Ocaml libraries for Xen tools +Requires: ocaml-runtime, xen-libs = %{epoch}:%{version}-%{release} + +%description ocaml +This package contains libraries for ocaml tools to manage Xen +virtual machines. + + +%package ocaml-devel +Summary: Ocaml development libraries for Xen tools +Requires: xen-ocaml = %{epoch}:%{version}-%{release} + +%description ocaml-devel +This package contains libraries for developing ocaml tools to +manage Xen virtual machines. +%endif + +%prep +%autosetup -p1 -n %{name}-%{upstream_version} + +# copy xen hypervisor .config file to change settings +cp -v %{SOURCE4} xen/.config + + +%build +# This package calls binutils components directly and would need to pass +# in flags to enable the LTO plugins +# Disable LTO +%define _lto_cflags %{nil} + +%if !%build_ocaml +%define ocaml_flags OCAML_TOOLS=n +%endif +%if %build_efi +%define efi_flags EFI_VENDOR=qubes +mkdir -p dist/install/boot/efi/efi/qubes +%endif +%if %build_ocaml +mkdir -p dist/install%{_libdir}/ocaml/stublibs +%endif +# BEGIN QUBES SPECIFIC PART +EXTRA_CFLAGS_XEN_TOOLS="$RPM_OPT_FLAGS $LDFLAGS" +%if 0%{?fedora} >= 37 +EXTRA_CFLAGS_XEN_TOOLS="$EXTRA_CFLAGS_XEN_TOOLS -Wno-error=use-after-free" +%endif +export EXTRA_CFLAGS_XEN_TOOLS +# END QUBES SPECIFIC PART +export EXTRA_CFLAGS_QEMU_TRADITIONAL="$RPM_OPT_FLAGS" +export EXTRA_CFLAGS_QEMU_XEN="$RPM_OPT_FLAGS" +export PYTHON="%{__python3}" +export LDFLAGS_SAVE=`echo $LDFLAGS | sed -e 's/-Wl,//g' -e 's/,/ /g' -e 's? -specs=[-a-z/0-9]*??g'` +export CFLAGS_SAVE="$CFLAGS" +%if %build_qemutrad +CONFIG_EXTRA="--enable-qemu-traditional" +%else +CONFIG_EXTRA="" +%endif +%if %build_ovmf +CONFIG_EXTRA="$CONFIG_EXTRA --with-system-ovmf=%{_libexecdir}/%{name}/boot/ovmf.bin" +%endif +%ifnarch armv7hl aarch64 +CONFIG_EXTRA="$CONFIG_EXTRA --with-system-ipxe=/usr/share/ipxe/10ec8139.rom" +%endif +%if %(test -f /usr/share/seabios/bios-256k.bin && echo 1|| echo 0) +CONFIG_EXTRA="$CONFIG_EXTRA --with-system-seabios=/usr/share/seabios/bios-256k.bin" +%else +CONFIG_EXTRA="$CONFIG_EXTRA --disable-seabios" +%endif +./configure --prefix=%{_prefix} --libdir=%{_libdir} --libexecdir=%{_libexecdir} --with-system-qemu=/usr/bin/qemu-system-i386 --with-linux-backend-modules="xen-evtchn xen-gntdev xen-gntalloc xen-blkback xen-netback xen-pciback xen-scsiback xen-acpi-processor" --enable-systemd --disable-pygrub $CONFIG_EXTRA +unset CFLAGS CXXFLAGS FFLAGS LDFLAGS +export LDFLAGS="$LDFLAGS_SAVE" +export CFLAGS="$CFLAGS_SAVE -Wno-error=address" + +%if %build_hyp +# QUBES SPECIFIC LINE +export CFLAGS=`echo $CFLAGS | sed -e 's/-specs=\/usr\/lib\/rpm\/redhat\/redhat-annobin-cc1//g'` +%if %build_crosshyp +export CFLAGS=`echo $CFLAGS | sed -e 's/-m32//g' -e 's/-march=i686//g' 's/-specs=\/usr\/lib\/rpm\/redhat\/redhat-annobin-cc1//g'` +XEN_TARGET_ARCH=x86_64 %make_build %{?efi_flags} prefix=/usr xen CC="/usr/bin/x86_64-linux-gnu-gcc" +%else +%ifarch armv7hl +export CFLAGS=`echo $CFLAGS | sed -e 's/-mfloat-abi=hard//g' -e 's/-march=armv7-a//g'` +%endif +# armv7hl aarch64 or x86_64 +%make_build %{?efi_flags} prefix=/usr xen +%endif +%endif +unset CFLAGS CXXFLAGS FFLAGS LDFLAGS + +# BEGIN QUBES SPECIFIC PART +%ifnarch armv7hl aarch64 +#CONFIG_EXTRA="$CONFIG_EXTRA --with-system-ipxe=/usr/share/ipxe" +CONFIG_EXTRA="$CONFIG_EXTRA --disable-ipxe --disable-rombios" +CONFIG_EXTRA="$CONFIG_EXTRA --disable-pvshim" +%endif +CONFIG_EXTRA="$CONFIG_EXTRA --with-system-qemu=/usr/bin/qemu-system-x86_64" +export PATH="/usr/bin:$PATH" +autoreconf -i +# END QUBES SPECIFIC PART + +%make_build %{?ocaml_flags} prefix=/usr tools +%if %build_docs +make prefix=/usr docs +%endif +export RPM_OPT_FLAGS_RED=`echo $RPM_OPT_FLAGS | sed -e 's/-m64//g' -e 's/--param=ssp-buffer-size=4//g' -e's/-fstack-protector-strong//'` +%ifarch %{ix86} +export EXTRA_CFLAGS_XEN_TOOLS="$RPM_OPT_FLAGS_RED" +%endif +%if %build_stubdom +%ifnarch armv7hl aarch64 +make mini-os-dir +make -C stubdom build +%endif +%ifarch x86_64 +export EXTRA_CFLAGS_XEN_TOOLS="$RPM_OPT_FLAGS_RED" +XEN_TARGET_ARCH=x86_32 make -C stubdom pv-grub-if-enabled +%endif +%endif + + +%install +rm -rf %{buildroot} +mkdir -p %{buildroot} +# copy all while preserving hardlinks between files +rsync -aH dist/install/ %{buildroot}/ +%if %build_stubdom +%ifnarch armv7hl aarch64 +make DESTDIR=%{buildroot} %{?ocaml_flags} prefix=/usr install-stubdom +%endif +%endif +%if %build_efi +# BEGIN QUBES SPECIFIC PART +mkdir -p %{buildroot}/boot/efi/efi/qubes +# END QUBES SPECIFIC PART +mv %{buildroot}/boot/efi/efi %{buildroot}/boot/efi/EFI +%endif +%if %build_xsm +# policy file should be in /boot/flask +mkdir %{buildroot}/boot/flask +mv %{buildroot}/boot/xenpolicy* %{buildroot}/boot/flask +%else +rm -f %{buildroot}/boot/xenpolicy* +# BEGIN QUBES SPECIFIC PART +rm -f %{buildroot}/usr/sbin/flask-* +# END QUBES SPECIFIC PART +%endif + +############ debug packaging: list files ############ + +find %{buildroot} -print | xargs ls -ld | sed -e 's|.*%{buildroot}||' > f1.list + +############ kill unwanted stuff ############ + +# stubdom: newlib +rm -rf %{buildroot}/usr/*-xen-elf + +# hypervisor symlinks +rm -rf %{buildroot}/boot/xen-%{hv_abi}.gz +rm -rf %{buildroot}/boot/xen-4.gz +rm -rf %{buildroot}/boot/xen.gz +%if !%build_hyp +rm -rf %{buildroot}/boot +%endif + +# silly doc dir fun +rm -fr %{buildroot}%{_datadir}/doc/xen +# BEGIN QUBES SPECIFIC PART +rm -rf %{buildroot}%{_datadir}/doc/qemu +# END QUBES SPECIFIC PART + +# Pointless helper +rm -f %{buildroot}%{_sbindir}/xen-python-path + +# qemu stuff (unused or available from upstream) +rm -rf %{buildroot}/usr/share/xen/man +rm -rf %{buildroot}/usr/bin/qemu-*-xen +# BEGIN QUBES SPECIFIC PART +# ln -s qemu-img %{buildroot}/%{_bindir}/qemu-img-xen +# ln -s qemu-img %{buildroot}/%{_bindir}/qemu-nbd-xen +# END QUBES SPECIFIC PART +for file in bios.bin openbios-sparc32 openbios-sparc64 ppc_rom.bin \ + pxe-e1000.bin pxe-ne2k_pci.bin pxe-pcnet.bin pxe-rtl8139.bin \ + vgabios.bin vgabios-cirrus.bin video.x openbios-ppc bamboo.dtb +do + rm -f %{buildroot}/%{_datadir}/xen/qemu/$file +done + +# README's not intended for end users +rm -f %{buildroot}/%{_sysconfdir}/xen/README* + +# standard gnu info files +rm -rf %{buildroot}/usr/info + +# adhere to Static Library Packaging Guidelines +rm -rf %{buildroot}/%{_libdir}/*.a + +%if %build_efi +# clean up extra efi files +%ifarch %{ix86} +rm -f %{buildroot}/usr/lib64/efi/xen-%{hv_abi}.efi +rm -f %{buildroot}/usr/lib64/efi/xen-4.efi +rm -f %{buildroot}/usr/lib64/efi/xen.efi +# cp -p %{buildroot}/usr/lib64/efi/xen-%{version}{,.notstripped}.efi +# strip -s %{buildroot}/usr/lib64/efi/xen-%{version}.efi +%else +rm -f %{buildroot}/%{_libdir}/efi/xen-%{hv_abi}.efi +rm -f %{buildroot}/%{_libdir}/efi/xen-4.efi +rm -f %{buildroot}/%{_libdir}/efi/xen.efi +# cp -p %{buildroot}/%{_libdir}/efi/xen-%{version}{,.notstripped}.efi +# strip -s %{buildroot}/%{_libdir}/efi/xen-%{version}.efi +%endif +%endif + +%if ! %build_ocaml +rm -rf %{buildroot}/%{_unitdir}/oxenstored.service +%endif + +%if %build_ovmf +cat /usr/share/OVMF/OVMF_{VARS,CODE}.fd >%{buildroot}%{_libexecdir}/%{name}/boot/ovmf.bin +%endif + +############ fixup files in /etc ############ + +# logrotate +mkdir -p %{buildroot}%{_sysconfdir}/logrotate.d/ +install -m 644 %{SOURCE2} %{buildroot}%{_sysconfdir}/logrotate.d/%{name} + +# init scripts +%define initdloc %(test -d /etc/rc.d/init.d/ && echo rc.d/init.d || echo init.d ) + +rm %{buildroot}%{_sysconfdir}/%{initdloc}/xen-watchdog +rm %{buildroot}%{_sysconfdir}/%{initdloc}/xencommons +rm %{buildroot}%{_sysconfdir}/%{initdloc}/xendomains +rm %{buildroot}%{_sysconfdir}/%{initdloc}/xendriverdomain + +# BEGIN QUBES SPECIFIC PART +rm %{buildroot}%{_sysconfdir}/sysconfig/xendomains +mkdir -p %{buildroot}/usr/lib/modules-load.d +cp %{SOURCE3} %{buildroot}/usr/lib/modules-load.d/xen.conf + +# get rid of standard domain starting scripts +rm %{buildroot}%{_unitdir}/xen-qemu-dom0-disk-backend.service +rm %{buildroot}%{_unitdir}/xendomains.service +# END QUBES SPECIFIC PART + +############ create dirs in /var ############ + +mkdir -p %{buildroot}%{_localstatedir}/lib/xen/images +mkdir -p %{buildroot}%{_localstatedir}/log/xen/console + +############ create symlink for x86_64 for compatibility with 4.4 ############ + +%if "%{_libdir}" != "/usr/lib" +ln -s %{_libexecdir}/%{name} %{buildroot}/%{_libdir}/%{name} +%endif + +# BEGIN QUBES SPECIFIC PART +# don't create symlink to qemu-system-i386 +ln -s ../sbin/xl %{buildroot}/%{_bindir}/xl +# END QUBES SPECIFIC PART + +############ debug packaging: list files ############ + +find %{buildroot} -print | xargs ls -ld | sed -e 's|.*%{buildroot}||' > f2.list +diff -u f1.list f2.list || true + +############ assemble license files ############ + +mkdir licensedir +# avoid licensedir to avoid recursion, also stubdom/ioemu and dist +# which are copies of files elsewhere +find . -path licensedir -prune -o -path stubdom/ioemu -prune -o \ + -path dist -prune -o -name COPYING -o -name LICENSE | while read file; do + mkdir -p licensedir/`dirname $file` + install -m 644 $file licensedir/$file +done + +############ all done now ############ + +# BEGIN QUBES SPECIFIC PART +%dnl %post +%dnl %if %with_systemd_presets +%dnl %systemd_post xendomains.service +%dnl %else +%dnl if [ $1 == 1 ]; then +%dnl /bin/systemctl enable xendomains.service +%dnl fi +%dnl %endif + +%dnl %preun +%dnl %if %with_systemd_presets +%dnl %systemd_preun xendomains.service +%dnl %else +%dnl if [ $1 == 0 ]; then +%dnl /bin/systemctl disable xendomains.service +%dnl fi +%dnl %endif +# END QUBES SPECIFIC PART + +%post runtime +%if %with_systemd_presets +# BEGIN QUBES SPECIFIC PART +%systemd_post xenstored.service xenconsoled.service xen-init-dom0.service +# END QUBES SPECIFIC PART +%else +if [ $1 == 1 ]; then + /bin/systemctl enable xenstored.service + /bin/systemctl enable xenconsoled.service +# BEGIN QUBES SPECIFIC PART + /bin/systemctl enable xen-init-dom0.service +# END QUBES SPECIFIC PART +fi +%endif + +%preun runtime +%if %with_systemd_presets +# BEGIN QUBES SPECIFIC PART +%systemd_preun xenstored.service xenconsoled.service xen-init-dom0.service +# END QUBES SPECIFIC PART +%else +if [ $1 == 0 ]; then + /bin/systemctl disable xenstored.service + /bin/systemctl disable xenconsoled.service +# BEGIN QUBES SPECIFIC PART + /bin/systemctl disable xen-init-dom0.service +# END QUBES SPECIFIC PART +fi +%endif + +%posttrans runtime +if [ ! -L /usr/lib/xen -a -d /usr/lib/xen -a -z "$(ls -A /usr/lib/xen)" ]; then + rmdir /usr/lib/xen +fi +if [ ! -e /usr/lib/xen ]; then + ln -s /usr/libexec/xen /usr/lib/xen +fi + +# QUBES SPECIFIC PART: next 2 lines (do not put comment before next section) +%post libs -p /sbin/ldconfig +%postun libs -p /sbin/ldconfig + +%if %build_hyp +%post hypervisor +%if %build_efi +XEN_EFI_VERSION=$(echo %{upstream_version} | sed -e 's/rc./rc/') +EFI_DIR=$(efibootmgr -v 2>/dev/null | awk ' + /^BootCurrent:/ { current=$2; } + /^Boot....\* / { + if ("Boot" current "*" == $1) { + sub(".*File\\(", ""); + sub("\\\\xen.efi\\).*", ""); + gsub("\\\\", "/"); + print; + } + }') +# FAT (on ESP) does not support symlinks +# override the file on purpose +if [ -n "${EFI_DIR}" -a -d "/boot/efi${EFI_DIR}" ]; then + cp -pf /boot/efi/EFI/qubes/xen-$XEN_EFI_VERSION.efi /boot/efi${EFI_DIR}/xen.efi +else + cp -pf /boot/efi/EFI/qubes/xen-$XEN_EFI_VERSION.efi /boot/efi/EFI/qubes/xen.efi +fi +%endif + +if [ -f /boot/efi/EFI/qubes/xen.cfg ]; then + if ! grep -q smt=off /boot/efi/EFI/qubes/xen.cfg; then + sed -i -e 's:^options=.*:\0 smt=off:' /boot/efi/EFI/qubes/xen.cfg + fi + if ! grep -q gnttab_max_frames /boot/efi/EFI/qubes/xen.cfg; then + sed -i -e 's:^options=.*:\0 gnttab_max_frames=2048 gnttab_max_maptrack_frames=4096:' /boot/efi/EFI/qubes/xen.cfg + fi +fi + +if [ -f /etc/default/grub ]; then + if ! grep -q smt=off /etc/default/grub; then + echo 'GRUB_CMDLINE_XEN_DEFAULT="$GRUB_CMDLINE_XEN_DEFAULT smt=off"' >> /etc/default/grub + grub2-mkconfig -o /boot/grub2/grub.cfg + fi + if ! grep -q gnttab_max_frames /etc/default/grub; then + echo 'GRUB_CMDLINE_XEN_DEFAULT="$GRUB_CMDLINE_XEN_DEFAULT gnttab_max_frames=2048 gnttab_max_maptrack_frames=4096"' >> /etc/default/grub + grub2-mkconfig -o /boot/grub2/grub.cfg + fi +fi + +if [ $1 == 1 -a -f /sbin/grub2-mkconfig ]; then + if [ -f /boot/grub2/grub.cfg ]; then + /sbin/grub2-mkconfig -o /boot/grub2/grub.cfg + fi + if [ -f /boot/efi/EFI/qubes/grub.cfg ] && \ + ! grep -q "configfile" /boot/efi/EFI/qubes/grub.cfg; then + /sbin/grub2-mkconfig -o /boot/efi/EFI/qubes/grub.cfg + fi +fi + +%postun hypervisor +if [ -f /sbin/grub2-mkconfig ]; then + if [ -f /boot/grub2/grub.cfg ]; then + /sbin/grub2-mkconfig -o /boot/grub2/grub.cfg + fi + if [ -f /boot/efi/EFI/qubes/grub.cfg ] && \ + ! grep -q "configfile" /boot/efi/EFI/qubes/grub.cfg; then + /sbin/grub2-mkconfig -o /boot/efi/EFI/qubes/grub.cfg + fi +fi +%endif + +%if %build_ocaml +%post ocaml +%if %with_systemd_presets +%systemd_post oxenstored.service +%else +if [ $1 == 1 ]; then + /bin/systemctl enable oxenstored.service +fi +%endif + +%preun ocaml +%if %with_systemd_presets +%systemd_preun oxenstored.service +%else +if [ $1 == 0 ]; then + /bin/systemctl disable oxenstored.service +fi +%endif +%endif + +# Base package only contains XenD/xm python stuff +#files -f xen-xm.lang +%files +%doc COPYING README + +# BEGIN QUBES SPECIFIC PART +%files -n python%{python3_pkgversion}-%{name} +%{python3_sitearch}/%{name} +%{python3_sitearch}/xen-*.egg-info +# END QUBES SPECIFIC PART + +%files libs +%{_libdir}/libxencall.so.1 +%{_libdir}/libxencall.so.1.3 +%{_libdir}/libxenctrl.so.4.* +%{_libdir}/libxendevicemodel.so.1 +%{_libdir}/libxendevicemodel.so.1.4 +%{_libdir}/libxenevtchn.so.1 +%{_libdir}/libxenevtchn.so.1.2 +%{_libdir}/libxenforeignmemory.so.1 +%{_libdir}/libxenforeignmemory.so.1.4 +%{_libdir}/libxengnttab.so.1 +%{_libdir}/libxengnttab.so.1.2 +%{_libdir}/libxenguest.so.4.* +%{_libdir}/libxenlight.so.4.* +%{_libdir}/libxenstat.so.4.* +%{_libdir}/libxenstore.so.4 +%{_libdir}/libxenstore.so.4.0 +%{_libdir}/libxentoolcore.so.1 +%{_libdir}/libxentoolcore.so.1.0 +%{_libdir}/libxentoollog.so.1 +%{_libdir}/libxentoollog.so.1.0 +%{_libdir}/libxenvchan.so.4.* +%{_libdir}/libxlutil.so.4.* +%{_libdir}/libxenhypfs.so.1 +%{_libdir}/libxenhypfs.so.1.0 + +# All runtime stuff except for XenD/xm python stuff +%files runtime +# Hotplug rules + +%dir %attr(0700,root,root) %{_sysconfdir}/%{name} +%dir %attr(0700,root,root) %{_sysconfdir}/%{name}/scripts/ +%config %attr(0700,root,root) %{_sysconfdir}/%{name}/scripts/* + +%{_sysconfdir}/bash_completion.d/xl + +%{_unitdir}/proc-xen.mount +%{_unitdir}/xenstored.service +%{_unitdir}/xenconsoled.service +%{_unitdir}/xen-watchdog.service +# BEGIN QUBES SPECIFIC PART +%{_unitdir}/xen-init-dom0.service +%exclude %{_unitdir}/xendriverdomain.service +# END QUBES SPECIFIC PART +/usr/lib/modules-load.d/xen.conf + +%config(noreplace) %{_sysconfdir}/sysconfig/xencommons +%config(noreplace) %{_sysconfdir}/xen/xl.conf +%config(noreplace) %{_sysconfdir}/xen/cpupool +%config(noreplace) %{_sysconfdir}/xen/xlexample* + +# Rotate console log files +%config(noreplace) %{_sysconfdir}/logrotate.d/xen + +# Programs run by other programs +%dir %{_libexecdir}/%{name} +%dir %{_libexecdir}/%{name}/bin +%attr(0700,root,root) %{_libexecdir}/%{name}/bin/* +# QEMU runtime files +%if %build_qemutrad +%ifnarch armv7hl aarch64 +%dir %{_datadir}/%{name}/qemu +%dir %{_datadir}/%{name}/qemu/keymaps +%{_datadir}/%{name}/qemu/keymaps/* +%endif +%endif + +# man pages +%if %build_docs +%{_mandir}/man1/xentop.1* +%{_mandir}/man8/xentrace.8* +%{_mandir}/man1/xl.1* +%{_mandir}/man5/xl.cfg.5* +%{_mandir}/man5/xl.conf.5* +%{_mandir}/man5/xlcpupool.cfg.5* +%{_mandir}/man1/xenstore* +%{_mandir}/man5/xl-disk-configuration.5.gz +%{_mandir}/man7/xen-pci-device-reservations.7.gz +%{_mandir}/man7/xen-tscmode.7.gz +%{_mandir}/man7/xen-vtpm.7.gz +%{_mandir}/man7/xen-vtpmmgr.7.gz +%{_mandir}/man5/xl-network-configuration.5.gz +%{_mandir}/man7/xen-pv-channel.7.gz +%{_mandir}/man7/xl-numa-placement.7.gz +%{_mandir}/man1/xenhypfs.1.gz +%{_mandir}/man7/xen-vbd-interface.7.gz +%{_mandir}/man5/xl-pci-configuration.5.gz +%endif + +# The firmware +%ifarch %{ix86} x86_64 +%dir %{_libexecdir}/%{name}/boot +%{_libexecdir}/xen/boot/hvmloader +%ifnarch %{ix86} +%{_libexecdir}/%{name}/boot/xen-shim +/usr/lib/debug%{_libexecdir}/xen/boot/xen-shim-syms +%endif +%if %build_ovmf +%{_libexecdir}/xen/boot/ovmf.bin +%endif +%if %build_stubdom +%if %build_qemutrad +%{_libexecdir}/xen/boot/ioemu-stubdom.gz +%endif +%{_libexecdir}/xen/boot/xenstore-stubdom.gz +%{_libexecdir}/xen/boot/xenstorepvh-stubdom.gz +%endif +%endif +%if "%{_libdir}" != "/usr/lib" +%{_libdir}/%{name} +%endif +%ghost /usr/lib/%{name} +# General Xen state +%dir %{_localstatedir}/lib/%{name} +%dir %{_localstatedir}/lib/%{name}/dump +%dir %{_localstatedir}/lib/%{name}/images +# Xenstore runtime state +%ghost %{_localstatedir}/run/xenstored + +# All xenstore CLI tools +%{_bindir}/xenstore +%{_bindir}/xenstore-* +%dnl %{_bindir}/remus +# Misc stuff +%ifnarch armv7hl aarch64 +%{_bindir}/xen-detect +%endif +%{_bindir}/xencov_split +%ifnarch armv7hl aarch64 +%{_sbindir}/gdbsx +%{_sbindir}/xen-kdd +%endif +%ifnarch armv7hl aarch64 +%{_sbindir}/xen-hptool +%{_sbindir}/xen-hvmcrash +%{_sbindir}/xen-hvmctx +%endif +%{_sbindir}/xenconsoled +%{_sbindir}/xenlockprof +%{_sbindir}/xenmon +%{_sbindir}/xentop +%{_sbindir}/xentrace_setmask +%{_sbindir}/xenbaked +%{_sbindir}/xenstored +%{_sbindir}/xenpm +%{_sbindir}/xenpmd +%{_sbindir}/xenperf +%{_sbindir}/xenwatchdogd +%{_sbindir}/xl +%ifnarch armv7hl aarch64 +%{_sbindir}/xen-lowmemd +%endif +%{_sbindir}/xencov +%ifnarch armv7hl aarch64 +%{_sbindir}/xen-mfndump +%endif +%{_bindir}/xenalyze +%{_sbindir}/xentrace +%{_sbindir}/xentrace_setsize +%ifnarch armv7hl aarch64 +%{_bindir}/xen-cpuid +%endif +%{_sbindir}/xen-livepatch +%{_sbindir}/xen-diag +%ifnarch armv7hl aarch64 +%{_sbindir}/xen-ucode +%{_sbindir}/xen-memshare +%{_sbindir}/xen-mceinj +%{_sbindir}/xen-vmtrace +%endif +%{_bindir}/vchan-socket-proxy +%{_sbindir}/xenhypfs +%{_sbindir}/xen-access + +# BEGIN QUBES SPECIFIC PART +%{_bindir}/xl +# END QUBES SPECIFIC PART + +# Xen logfiles +%dir %attr(0700,root,root) %{_localstatedir}/log/xen +# Guest/HV console logs +%dir %attr(0700,root,root) %{_localstatedir}/log/xen/console + +%files hypervisor +%if %build_hyp +%defattr(-,root,root) +%ifnarch armv7hl aarch64 +/boot/xen-*.gz +# BEGIN QUBES SPECIFIC PART +# /boot/xen.gz +# END QUBES SPECIFIC PART +/boot/xen*.config +%else +/boot/xen* +%endif +%if %build_xsm +%dir %attr(0755,root,root) /boot/flask +/boot/flask/xenpolicy* +%endif +%if %build_efi +/boot/efi/EFI/qubes/*.efi +%endif +/usr/lib/debug/xen* +%endif + +%if %build_docs +%files doc +%doc docs/misc/ +%doc dist/install/usr/share/doc/xen/html +%endif + +%files devel +%{_includedir}/*.h +%dir %{_includedir}/xen +%{_includedir}/xen/* +%dir %{_includedir}/xenstore-compat +%{_includedir}/xenstore-compat/* +%{_libdir}/*.so +%{_libdir}/pkgconfig/* + +%files licenses +%doc licensedir/* + +%if %build_ocaml +%files ocaml +%{_libdir}/ocaml/xen* +%exclude %{_libdir}/ocaml/xen*/*.a +%exclude %{_libdir}/ocaml/xen*/*.cmxa +%exclude %{_libdir}/ocaml/xen*/*.cmx +%{_libdir}/ocaml/stublibs/*.so +%{_libdir}/ocaml/stublibs/*.so.owner +%{_sbindir}/oxenstored +%config(noreplace) %{_sysconfdir}/xen/oxenstored.conf +%{_unitdir}/oxenstored.service + +%files ocaml-devel +%{_libdir}/ocaml/xen*/*.a +%{_libdir}/ocaml/xen*/*.cmxa +%{_libdir}/ocaml/xen*/*.cmx +%endif + +%changelog +@CHANGELOG@ + diff --git a/xen/arch/x86/Makefile b/xen/arch/x86/Makefile index b35fd5196c..66e10ad9e2 100644 --- a/xen/arch/x86/Makefile +++ b/xen/arch/x86/Makefile @@ -57,14 +57,17 @@ obj-y += pci.o obj-y += physdev.o obj-$(CONFIG_COMPAT) += x86_64/physdev.o obj-$(CONFIG_X86_PSR) += psr.o +obj-y += intel_txt.o obj-y += setup.o obj-y += shutdown.o +obj-y += slaunch.o obj-y += smp.o obj-y += smpboot.o obj-y += spec_ctrl.o obj-y += srat.o obj-y += string.o obj-y += time.o +obj-y += tpm.o obj-y += traps.o obj-$(CONFIG_INTEL) += tsx.o obj-y += usercopy.o diff --git a/xen/arch/x86/acpi/cpu_idle.c b/xen/arch/x86/acpi/cpu_idle.c index 876317fad0..46adda8755 100644 --- a/xen/arch/x86/acpi/cpu_idle.c +++ b/xen/arch/x86/acpi/cpu_idle.c @@ -1244,7 +1244,7 @@ int get_cpu_id(u32 acpi_id) for ( i = 0; i < nr_cpu_ids; i++ ) { - if ( apic_id == x86_cpu_to_apicid[i] ) + if ( apic_id == cpu_physical_id(i) ) return i; } @@ -1350,7 +1350,7 @@ long set_cx_pminfo(uint32_t acpi_id, struct xen_processor_power *power) if ( !cpu_online(cpu_id) ) { - uint32_t apic_id = x86_cpu_to_apicid[cpu_id]; + uint32_t apic_id = cpu_physical_id(cpu_id); /* * If we've just learned of more available C states, wake the CPU if diff --git a/xen/arch/x86/acpi/lib.c b/xen/arch/x86/acpi/lib.c index 51cb082ca0..87a1f38f3f 100644 --- a/xen/arch/x86/acpi/lib.c +++ b/xen/arch/x86/acpi/lib.c @@ -91,7 +91,7 @@ unsigned int acpi_get_processor_id(unsigned int cpu) { unsigned int acpiid, apicid; - if ((apicid = x86_cpu_to_apicid[cpu]) == BAD_APICID) + if ((apicid = cpu_physical_id(cpu)) == BAD_APICID) return INVALID_ACPIID; for (acpiid = 0; acpiid < ARRAY_SIZE(x86_acpiid_to_apicid); acpiid++) diff --git a/xen/arch/x86/apic.c b/xen/arch/x86/apic.c index bb86a1c161..feae06f57c 100644 --- a/xen/arch/x86/apic.c +++ b/xen/arch/x86/apic.c @@ -948,7 +948,7 @@ void __init init_apic_mappings(void) */ if (boot_cpu_physical_apicid == -1U) boot_cpu_physical_apicid = get_apic_id(); - x86_cpu_to_apicid[0] = get_apic_id(); + cpu_physical_id(0) = get_apic_id(); ioapic_init(); } diff --git a/xen/arch/x86/arch.mk b/xen/arch/x86/arch.mk index 9dde8a5756..1e2caa2f3e 100644 --- a/xen/arch/x86/arch.mk +++ b/xen/arch/x86/arch.mk @@ -95,7 +95,9 @@ XEN_BUILD_EFI := $(call if-success,$(CC) $(filter-out -include %/include/xen/con -c $(srctree)/$(efi-check).c -o $(efi-check).o,y) # Check if the linker supports PE. -EFI_LDFLAGS := $(patsubst -m%,-mi386pep,$(LDFLAGS)) --subsystem=10 --enable-long-section-names +#EFI_LDFLAGS := $(patsubst -m%,-mi386pep,$(LDFLAGS)) --subsystem=10 --enable-long-section-names +# use a reduced set of options from LDFLAGS +EFI_LDFLAGS = --as-needed --build-id=sha1 -mi386pep --subsystem=10 --enable-long-section-names LD_PE_check_cmd = $(call ld-option,$(EFI_LDFLAGS) --image-base=0x100000000 -o $(efi-check).efi $(efi-check).o) XEN_BUILD_PE := $(LD_PE_check_cmd) diff --git a/xen/arch/x86/boot/Makefile b/xen/arch/x86/boot/Makefile index d457876659..5b4a9129f9 100644 --- a/xen/arch/x86/boot/Makefile +++ b/xen/arch/x86/boot/Makefile @@ -5,6 +5,8 @@ obj-bin-y += $(obj64) obj32 := cmdline.32.o obj32 += reloc.32.o obj32 += reloc-trampoline.32.o +obj32 += slaunch_early.32.o +obj32 += tpm_early.32.o obj64 := reloc-trampoline.o @@ -27,6 +29,10 @@ $(obj32): XEN_CFLAGS := $(CFLAGS_x86_32) -fpic $(obj)/%.32.o: $(src)/%.c FORCE $(call if_changed_rule,cc_o_c) +$(obj)/tpm_early.32.o: XEN_CFLAGS += -D__EARLY_TPM__ +$(obj)/tpm_early.32.o: $(src)/../tpm.c FORCE + $(call if_changed_rule,cc_o_c) + orphan-handling-$(call ld-option,--orphan-handling=error) := --orphan-handling=error LDFLAGS_DIRECT-$(call ld-option,--warn-rwx-segments) := --no-warn-rwx-segments LDFLAGS_DIRECT += $(LDFLAGS_DIRECT-y) @@ -41,7 +47,7 @@ LD32 := $(LD) $(subst x86_64,i386,$(LDFLAGS_DIRECT)) # is greater than 2^16 so that any 16bit relocations if present in the object # file turns into a build-time error. text_gap := 0x010200 -text_diff := 0x408020 +text_diff := 0x408040 $(obj)/build32.base.lds: AFLAGS-y += -DGAP=$(text_gap) -DTEXT_DIFF=$(text_diff) $(obj)/build32.offset.lds: AFLAGS-y += -DGAP=$(text_gap) -DTEXT_DIFF=$(text_diff) -DAPPLY_OFFSET @@ -80,7 +86,7 @@ cmd_combine = \ --bin1 $(obj)/built-in-32.base.bin \ --bin2 $(obj)/built-in-32.offset.bin \ --map $(obj)/built-in-32.base.map \ - --exports cmdline_parse_early,reloc,reloc_trampoline32 \ + --exports cmdline_parse_early,reloc,reloc_trampoline32,slaunch_early_tests,tpm_extend_mbi \ --output $@ targets += built-in-32.S diff --git a/xen/arch/x86/boot/head.S b/xen/arch/x86/boot/head.S index 1b3bd16fe5..32ef12de89 100644 --- a/xen/arch/x86/boot/head.S +++ b/xen/arch/x86/boot/head.S @@ -4,9 +4,11 @@ #include #include #include +#include #include #include #include +#include #include #include @@ -126,6 +128,25 @@ multiboot2_header: .size multiboot2_header, . - multiboot2_header .type multiboot2_header, @object + .balign 16 +mle_header: + .long 0x9082ac5a /* UUID0 */ + .long 0x74a7476f /* UUID1 */ + .long 0xa2555c0f /* UUID2 */ + .long 0x42b651cb /* UUID3 */ + .long 0x00000034 /* MLE header size */ + .long 0x00020002 /* MLE version 2.2 */ + .long (slaunch_stub_entry - start) /* Linear entry point of MLE (SINIT virt. address) */ + .long 0x00000000 /* First valid page of MLE */ + .long 0x00000000 /* Offset within binary of first byte of MLE */ + .long (_end - start) /* Offset within binary of last byte + 1 of MLE */ + .long 0x00000223 /* Bit vector of MLE-supported capabilities */ + .long 0x00000000 /* Starting linear address of command line (unused) */ + .long 0x00000000 /* Ending linear address of command line (unused) */ + + .size mle_header, .-mle_header + .type mle_header, @object + .section .init.rodata, "a", @progbits .Lbad_cpu_msg: .asciz "ERR: Not a 64-bit CPU!" @@ -331,6 +352,61 @@ cs32_switch: /* Jump to earlier loaded address. */ jmp *%edi + /* + * Entry point for TrenchBoot Secure Launch, common for Intel TXT and + * AMD Secure Startup, but state is slightly different. + * + * On Intel: + * CPU is in 32b protected mode with paging disabled. On entry: + * - %ebx = %eip = this entry point, + * - stack pointer is undefined, + * - CS is flat 4GB code segment, + * - DS, ES, SS, FS and GS are undefined according to TXT SDG, but this + * would make it impossible to initialize GDTR, because GDT base must + * be relocated in the descriptor, which requires write access that + * CS doesn't provide. Instead we have to assume that DS is set by + * SINIT ACM as flat 4GB data segment. + * + * Additional restrictions: + * - some MSRs are partially cleared, among them IA32_MISC_ENABLE, so + * some capabilities might be reported as disabled even if they are + * supported by CPU + * - interrupts (including NMIs and SMIs) are disabled and must be + * enabled later + * - trying to enter real mode results in reset + * - APs must be brought up by MONITOR or GETSEC[WAKEUP], depending on + * which is supported by a given SINIT ACM + * + * On AMD (as implemented by TrenchBoot's SKL): + * CPU is in 32b protected mode with paging disabled. On entry: + * - %ebx = %eip = this entry point, + * - %ebp holds base address of SKL + * - stack pointer is treated as undefined for parity with TXT, + * - CS is flat 4GB code segment, + * - DS, ES, SS are flat 4GB data segments, but treated as undefined for + * parity with TXT. + * + * Additional restrictions: + * - interrupts (including NMIs and SMIs) are disabled and must be + * enabled later + * - APs must be brought up by SIPI without an INIT + */ +slaunch_stub_entry: + /* Calculate the load base address. */ + mov %ebx, %esi + sub $sym_offs(slaunch_stub_entry), %esi + + /* On AMD, %ebp holds the base address of SLB, save it for later. */ + mov %ebp, %ebx + + /* + * Mark Secure Launch boot protocol and jump to common entry. Note that + * all general purpose registers except %ebx and %esi are clobbered + * between here and .Lslaunch_proto. + */ + mov $SLAUNCH_BOOTLOADER_MAGIC, %eax + jmp .Lset_stack + #ifdef CONFIG_PVH_GUEST ELFNOTE(Xen, XEN_ELFNOTE_PHYS32_ENTRY, .long sym_offs(__pvh_start)) @@ -370,6 +446,7 @@ __start: /* Restore the clobbered field. */ mov %edx, (%ebx) +.Lset_stack: /* Set up stack. */ lea STACK_SIZE - CPUINFO_sizeof + sym_esi(cpu0_stack), %esp @@ -418,6 +495,10 @@ __start: /* Bootloaders may set multiboot{1,2}.mem_lower to a nonzero value. */ xor %edx,%edx + /* Check for TrenchBoot slaunch bootloader. */ + cmp $SLAUNCH_BOOTLOADER_MAGIC,%eax + je .Lslaunch_proto + /* Check for Multiboot2 bootloader. */ cmp $MULTIBOOT2_BOOTLOADER_MAGIC,%eax je .Lmultiboot2_proto @@ -433,6 +514,43 @@ __start: cmovnz MB_mem_lower(%ebx),%edx jmp trampoline_bios_setup +.Lslaunch_proto: + /* Upon reaching here, CPU state mostly matches the one setup by the + * bootloader with ESP, ESI and EDX being clobbered above. */ + + /* Save information that TrenchBoot slaunch was used. */ + movb $1, sym_esi(slaunch_active) + + /* Prepare space for output parameter of slaunch_early_tests(), which is + * a structure of two uint32_t fields. */ + sub $8, %esp + + push %esp /* pointer to output structure */ + push %ebx /* Slaunch parameter on AMD */ + lea sym_offs(__2M_rwdata_end), %ecx /* end of target image */ + lea sym_offs(_start), %edx /* target base address */ + mov %esi, %eax /* load base address */ + /* slaunch_early_tests(load/eax, tgt/edx, tgt_end/ecx, + slaunch/stk, ret/stk) using fastcall. */ + call slaunch_early_tests + add $8, %esp + + /* Move outputs of slaunch_early_tests() from stack into registers. */ + pop %eax /* physical MBI address */ + pop %edx /* physical SLRT address */ + + /* Save physical address of SLRT for C code. */ + mov %edx, sym_esi(slaunch_slrt) + + /* Store MBI address in EBX where MB2 code expects it. */ + mov %eax, %ebx + + /* tpm_extend_mbi(mbi/eax, slrt/edx) using fastcall. */ + call tpm_extend_mbi + + /* Move magic number expected by Multiboot 2 to EAX and fall through. */ + movl $MULTIBOOT2_BOOTLOADER_MAGIC,%eax + .Lmultiboot2_proto: /* Skip Multiboot2 information fixed part. */ lea (MB2_fixed_sizeof+MULTIBOOT2_TAG_ALIGN-1)(%ebx),%ecx diff --git a/xen/arch/x86/boot/reloc-trampoline.c b/xen/arch/x86/boot/reloc-trampoline.c index d5548eb08f..e35e7c78aa 100644 --- a/xen/arch/x86/boot/reloc-trampoline.c +++ b/xen/arch/x86/boot/reloc-trampoline.c @@ -2,13 +2,15 @@ #include #include + +#include #include extern const int32_t __trampoline_rel_start[], __trampoline_rel_stop[]; extern const int32_t __trampoline_seg_start[], __trampoline_seg_stop[]; #if defined(__i386__) -void reloc_trampoline32(void) +void asmlinkage reloc_trampoline32(void) #elif defined (__x86_64__) void reloc_trampoline64(void) #else diff --git a/xen/arch/x86/boot/slaunch_early.c b/xen/arch/x86/boot/slaunch_early.c new file mode 100644 index 0000000000..5296fbe403 --- /dev/null +++ b/xen/arch/x86/boot/slaunch_early.c @@ -0,0 +1,178 @@ +/* + * Copyright (c) 2022-2024 3mdeb Sp. z o.o. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#include +#include +#include +#include +#include + +struct early_tests_results +{ + uint32_t mbi_pa; + uint32_t slrt_pa; +} __packed; + +static bool is_intel_cpu(void) +{ + /* + * asm/processor.h can't be included in early code, which means neither + * cpuid() function nor boot_cpu_data can be used here. + */ + uint32_t eax, ebx, ecx, edx; + asm volatile ( "cpuid" + : "=a" (eax), "=b" (ebx), "=c" (ecx), "=d" (edx) + : "0" (0), "c" (0) ); + return ebx == X86_VENDOR_INTEL_EBX + && ecx == X86_VENDOR_INTEL_ECX + && edx == X86_VENDOR_INTEL_EDX; +} + +static void verify_pmr_ranges(struct txt_os_mle_data *os_mle, + struct txt_os_sinit_data *os_sinit, + uint32_t load_base_addr, uint32_t tgt_base_addr, + uint32_t xen_size) +{ + int check_high_pmr = 0; + + /* Verify the value of the low PMR base. It should always be 0. */ + if ( os_sinit->vtd_pmr_lo_base != 0 ) + txt_reset(SLAUNCH_ERROR_LO_PMR_BASE); + + /* + * Low PMR size should not be 0 on current platforms. There is an ongoing + * transition to TPR-based DMA protection instead of PMR-based; this is not + * yet supported by the code. + */ + if ( os_sinit->vtd_pmr_lo_size == 0 ) + txt_reset(SLAUNCH_ERROR_LO_PMR_SIZE); + + /* Check if regions overlap. Treat regions with no hole between as error. */ + if ( os_sinit->vtd_pmr_hi_size != 0 && + os_sinit->vtd_pmr_hi_base <= os_sinit->vtd_pmr_lo_size ) + txt_reset(SLAUNCH_ERROR_HI_PMR_BASE); + + /* All regions accessed by 32b code must be below 4G. */ + if ( os_sinit->vtd_pmr_hi_base + os_sinit->vtd_pmr_hi_size <= + 0x100000000ull ) + check_high_pmr = 1; + + /* + * ACM checks that TXT heap and MLE memory is protected against DMA. We have + * to check if MBI and whole Xen memory is protected. The latter is done in + * case bootloader failed to set whole image as MLE and to make sure that + * both pre- and post-relocation code is protected. + */ + + /* Check if all of Xen before relocation is covered by PMR. */ + if ( !is_in_pmr(os_sinit, load_base_addr, xen_size, check_high_pmr) ) + txt_reset(SLAUNCH_ERROR_LO_PMR_MLE); + + /* Check if all of Xen after relocation is covered by PMR. */ + if ( load_base_addr != tgt_base_addr && + !is_in_pmr(os_sinit, tgt_base_addr, xen_size, check_high_pmr) ) + txt_reset(SLAUNCH_ERROR_LO_PMR_MLE); + + /* Check if MBI is covered by PMR. MBI starts with 'uint32_t total_size'. */ + if ( !is_in_pmr(os_sinit, os_mle->boot_params_addr, + *(uint32_t *)os_mle->boot_params_addr, check_high_pmr) ) + txt_reset(SLAUNCH_ERROR_BUFFER_BEYOND_PMR); + + /* Check if TPM event log (if present) is covered by PMR. */ + /* + * FIXME: currently commented out as GRUB allocates it in a hole between + * PMR and reserved RAM, due to 2MB resolution of PMR. There are no other + * easy-to-use DMA protection mechanisms that would allow to protect that + * part of memory. TPR (TXT DMA Protection Range) gives 1MB resolution, but + * it still wouldn't be enough. + * + * One possible solution would be for GRUB to allocate log at lower address, + * but this would further increase memory space fragmentation. Another + * option is to align PMR up instead of down, making PMR cover part of + * reserved region, but it is unclear what the consequences may be. + * + * In tboot this issue was resolved by reserving leftover chunks of memory + * in e820 and/or UEFI memory map. This is also a valid solution, but would + * require more changes to GRUB than the ones listed above, as event log is + * allocated much earlier than PMRs. + */ + /* + if ( os_mle->evtlog_addr != 0 && os_mle->evtlog_size != 0 && + !is_in_pmr(os_sinit, os_mle->evtlog_addr, os_mle->evtlog_size, + check_high_pmr) ) + txt_reset(SLAUNCH_ERROR_BUFFER_BEYOND_PMR); + */ +} + +void slaunch_early_tests(uint32_t load_base_addr, + uint32_t tgt_base_addr, + uint32_t tgt_end_addr, + uint32_t slaunch_param, + struct early_tests_results *result) +{ + void *txt_heap; + struct txt_os_mle_data *os_mle; + struct txt_os_sinit_data *os_sinit; + uint32_t size = tgt_end_addr - tgt_base_addr; + + if ( !is_intel_cpu() ) + { + /* + * Not an Intel CPU. Currently the only other option is AMD with SKINIT + * and secure-kernel-loader. + */ + struct slr_table *slrt; + struct slr_entry_dl_info *dl_info; + + const uint16_t *sl_header = (void *)slaunch_param; + /* + * The fourth 16-bit integer of SKL's header is an offset to + * bootloader's data, which is SLRT. + */ + result->slrt_pa = slaunch_param + sl_header[3]; + slrt = (struct slr_table *)result->slrt_pa; + + result->mbi_pa = 0; + dl_info = (struct slr_entry_dl_info *) + slr_next_entry_by_tag (slrt, NULL, SLR_ENTRY_DL_INFO); + /* Basic checks only, SKL checked and consumed the rest. */ + if ( dl_info == NULL + || dl_info->hdr.size != sizeof(*dl_info) + || dl_info->bl_context.bootloader != SLR_BOOTLOADER_GRUB ) + return; + + result->mbi_pa = dl_info->bl_context.context; + return; + } + + /* Clear the TXT error registers for a clean start of day */ + write_txt_reg(TXTCR_ERRORCODE, 0); + + txt_heap = _p(read_txt_reg(TXTCR_HEAP_BASE)); + + if ( txt_os_mle_data_size(txt_heap) < sizeof(*os_mle) || + txt_os_sinit_data_size(txt_heap) < sizeof(*os_sinit) ) + txt_reset(SLAUNCH_ERROR_GENERIC); + + os_mle = txt_os_mle_data_start(txt_heap); + os_sinit = txt_os_sinit_data_start(txt_heap); + + verify_pmr_ranges(os_mle, os_sinit, load_base_addr, tgt_base_addr, size); + + result->mbi_pa = os_mle->boot_params_addr; + result->slrt_pa = os_mle->slrt; +} diff --git a/xen/arch/x86/boot/trampoline.S b/xen/arch/x86/boot/trampoline.S index 924bda37c1..3fd6d1bf2c 100644 --- a/xen/arch/x86/boot/trampoline.S +++ b/xen/arch/x86/boot/trampoline.S @@ -59,6 +59,16 @@ GLOBAL(trampoline_realmode_entry) ljmpl $BOOT_CS32,$bootsym_rel(trampoline_protmode_entry,6) .code32 +GLOBAL(txt_ap_entry) + /* + * APs enter here in protected mode without paging. GDT is set in JOIN + * structure, it points to trampoline_gdt. Interrupts are disabled by + * TXT (including NMI and SMI), so IDT doesn't matter at this point. + * The only missing point is telling that we are AP by saving non-zero + * value in EBX. + */ + mov $1, %ebx + trampoline_protmode_entry: /* Set up a few descriptors: on entry only CS is guaranteed good. */ mov $BOOT_DS,%eax @@ -72,6 +82,27 @@ trampoline_protmode_entry: mov $X86_CR4_PAE,%ecx mov %ecx,%cr4 + /* + * Get APIC ID while we're in non-paged mode. Start by checking if + * x2APIC is enabled. + */ + mov $MSR_APIC_BASE, %ecx + rdmsr + test $APIC_BASE_EXTD, %eax + jnz .Lx2apic + + /* Not x2APIC, read from MMIO */ + and $APIC_BASE_ADDR_MASK, %eax + mov APIC_ID(%eax), %esp + shr $24, %esp + jmp 1f + +.Lx2apic: + mov $(MSR_X2APIC_FIRST + (APIC_ID >> MSR_X2APIC_SHIFT)), %ecx + rdmsr + mov %eax, %esp +1: + /* Load pagetable base register. */ mov $sym_offs(idle_pg_table),%eax add bootsym_rel(trampoline_xen_phys_start,4,%eax) @@ -123,7 +154,7 @@ start64: .word 0 idt_48: .word 0, 0, 0 # base = limit = 0 -trampoline_gdt: +GLOBAL(trampoline_gdt) .word 0 /* 0x0000: unused (reused for GDTR) */ gdt_48: .word .Ltrampoline_gdt_end - trampoline_gdt - 1 @@ -134,6 +165,13 @@ gdt_48: .quad 0x00cf93000000ffff /* 0x0018: ring 0 data */ .quad 0x00009b000000ffff /* 0x0020: real-mode code @ BOOT_TRAMPOLINE */ .quad 0x000093000000ffff /* 0x0028: real-mode data @ BOOT_TRAMPOLINE */ + /* + * Intel TXT requires these two in exact order. This isn't compatible + * with order required by syscall, so we have duplicated entries... + * If order ever changes, update selector numbers in asm/intel_txt.h. + */ + .quad 0x00cf9b000000ffff /* 0x0030: ring 0 code, 32-bit mode */ + .quad 0x00cf93000000ffff /* 0x0038: ring 0 data */ .Ltrampoline_gdt_end: /* Relocations for trampoline Real Mode segments. */ diff --git a/xen/arch/x86/boot/x86_64.S b/xen/arch/x86/boot/x86_64.S index 26b9d1c2df..8c902ae7cd 100644 --- a/xen/arch/x86/boot/x86_64.S +++ b/xen/arch/x86/boot/x86_64.S @@ -15,7 +15,38 @@ ENTRY(__high_start) mov $XEN_MINIMAL_CR4,%rcx mov %rcx,%cr4 - mov stack_start(%rip),%rsp + test %ebx,%ebx + cmovz stack_start(%rip), %rsp + jz .L_stack_set + + /* APs only: get stack base from APIC ID saved in %esp. */ + mov $0, %rbx + lea cpu_data(%rip), %rcx + /* cpu_data[0] is BSP, skip it. */ +1: + add $1, %rbx + add $CPUINFO_X86_sizeof, %rcx + cmp $NR_CPUS, %rbx + jb 2f + hlt +2: + cmp %esp, CPUINFO_X86_apicid(%rcx) + jne 1b + + /* + * %rbx (callee-save, as it will be passed to start_secondary() later) + * is now Xen CPU index. Use it to read stack base. + */ + lea stack_base(%rip), %rcx + mov (%rcx, %rbx, 8), %rsp + + test %rsp,%rsp + jnz 1f + hlt +1: + add $(STACK_SIZE - CPUINFO_sizeof), %rsp + +.L_stack_set: /* Reset EFLAGS (subsumes CLI and CLD). */ pushq $0 @@ -74,6 +105,7 @@ ENTRY(__high_start) .L_ap_cet_done: #endif /* CONFIG_XEN_SHSTK || CONFIG_XEN_IBT */ + mov %rbx, %rdi tailcall start_secondary .L_bsp: diff --git a/xen/arch/x86/cpu/microcode/core.c b/xen/arch/x86/cpu/microcode/core.c index 22bc0875eb..4811b5ffb1 100644 --- a/xen/arch/x86/cpu/microcode/core.c +++ b/xen/arch/x86/cpu/microcode/core.c @@ -688,8 +688,8 @@ static int __init cf_check microcode_init_cache(void) /* early_microcode_load() didn't leave us any work to do. */ return 0; - size = bi->mods[early_mod_idx].mod->mod_end; - data = __mfn_to_virt(bi->mods[early_mod_idx].mod->mod_start); + size = bi->mods[early_mod_idx].size; + data = __va(bi->mods[early_mod_idx].start); /* * If opt_scan is set, we're looking for a CPIO archive rather than a raw @@ -765,7 +765,7 @@ static int __init early_microcode_load(struct boot_info *bi) bm->type != BOOTMOD_RAMDISK ) continue; - size = bm->mod->mod_end; + size = bm->size; data = bootstrap_map_bm(bm); if ( !data ) { @@ -819,7 +819,7 @@ static int __init early_microcode_load(struct boot_info *bi) } bi->mods[idx].type = BOOTMOD_MICROCODE; - size = bi->mods[idx].mod->mod_end; + size = bi->mods[idx].size; data = bootstrap_map_bm(&bi->mods[idx]); if ( !data ) { diff --git a/xen/arch/x86/cpu/mwait-idle.c b/xen/arch/x86/cpu/mwait-idle.c index 9c16cc166a..ff5ce2ec56 100644 --- a/xen/arch/x86/cpu/mwait-idle.c +++ b/xen/arch/x86/cpu/mwait-idle.c @@ -1180,8 +1180,8 @@ static void __init ivt_idle_state_table_update(void) unsigned int cpu, max_apicid = boot_cpu_physical_apicid; for_each_present_cpu(cpu) - if (max_apicid < x86_cpu_to_apicid[cpu]) - max_apicid = x86_cpu_to_apicid[cpu]; + if (max_apicid < cpu_physical_id(cpu)) + max_apicid = cpu_physical_id(cpu); switch (apicid_to_socket(max_apicid)) { case 0: case 1: /* 1 and 2 socket systems use default ivt_cstates */ diff --git a/xen/arch/x86/domain.c b/xen/arch/x86/domain.c index 78a13e6812..c7142d25e8 100644 --- a/xen/arch/x86/domain.c +++ b/xen/arch/x86/domain.c @@ -1636,7 +1636,7 @@ long do_vcpu_op(int cmd, unsigned int vcpuid, XEN_GUEST_HANDLE_PARAM(void) arg) break; cpu_id.phys_id = - (uint64_t)x86_cpu_to_apicid[v->vcpu_id] | + (uint64_t)cpu_physical_id(v->vcpu_id) | ((uint64_t)acpi_get_processor_id(v->vcpu_id) << 32); rc = -EFAULT; diff --git a/xen/arch/x86/e820.c b/xen/arch/x86/e820.c index e052e84de7..5241f2bb58 100644 --- a/xen/arch/x86/e820.c +++ b/xen/arch/x86/e820.c @@ -11,6 +11,8 @@ #include #include #include +#include +#include /* * opt_mem: Limit maximum address of physical RAM. @@ -455,6 +457,9 @@ static uint64_t __init mtrr_top_of_ram(void) rdmsrl(MSR_MTRRcap, mtrr_cap); rdmsrl(MSR_MTRRdefType, mtrr_def); + if ( slaunch_active && boot_cpu_data.x86_vendor == X86_VENDOR_INTEL ) + txt_restore_mtrrs(e820_verbose); + if ( e820_verbose ) printk(" MTRR cap: %"PRIx64" type: %"PRIx64"\n", mtrr_cap, mtrr_def); diff --git a/xen/arch/x86/efi/efi-boot.h b/xen/arch/x86/efi/efi-boot.h index 9d3f2b7144..1d8902a9a7 100644 --- a/xen/arch/x86/efi/efi-boot.h +++ b/xen/arch/x86/efi/efi-boot.h @@ -4,6 +4,8 @@ * therefore can define arch specific global variables. */ #include + +#include #include #include #include @@ -103,8 +105,6 @@ static void __init efi_arch_relocate_image(unsigned long delta) } } -void reloc_trampoline64(void); - static void __init relocate_trampoline(unsigned long phys) { trampoline_phys = phys; diff --git a/xen/arch/x86/hvm/dom0_build.c b/xen/arch/x86/hvm/dom0_build.c index 35d8d6a070..ce5b5c31b3 100644 --- a/xen/arch/x86/hvm/dom0_build.c +++ b/xen/arch/x86/hvm/dom0_build.c @@ -649,8 +649,8 @@ static int __init pvh_load_kernel( { void *image_base = bootstrap_map_bm(image); void *image_start = image_base + image->headroom; - unsigned long image_len = image->mod->mod_end; - unsigned long initrd_len = initrd ? initrd->mod->mod_end : 0; + unsigned long image_len = image->size; + unsigned long initrd_len = initrd ? initrd->size : 0; const char *cmdline = image->cmdline_pa ? __va(image->cmdline_pa) : NULL; struct elf_binary elf; struct elf_dom_parms parms; @@ -726,7 +726,7 @@ static int __init pvh_load_kernel( if ( initrd != NULL ) { - rc = hvm_copy_to_guest_phys(last_addr, mfn_to_virt(initrd->mod->mod_start), + rc = hvm_copy_to_guest_phys(last_addr, __va(initrd->start), initrd_len, v); if ( rc ) { @@ -755,7 +755,7 @@ static int __init pvh_load_kernel( } /* Free temporary buffers. */ - discard_initial_images(); + free_boot_modules(); if ( cmdline != NULL ) { diff --git a/xen/arch/x86/include/asm/apicdef.h b/xen/arch/x86/include/asm/apicdef.h index 49e29ec801..f687d30998 100644 --- a/xen/arch/x86/include/asm/apicdef.h +++ b/xen/arch/x86/include/asm/apicdef.h @@ -119,6 +119,10 @@ #define MAX_IO_APICS 128 +#ifndef __ASSEMBLY__ + extern bool x2apic_enabled; +#endif /* !__ASSEMBLY__ */ + #endif diff --git a/xen/arch/x86/include/asm/asm_defns.h b/xen/arch/x86/include/asm/asm_defns.h index 92b4116a15..2a612c7003 100644 --- a/xen/arch/x86/include/asm/asm_defns.h +++ b/xen/arch/x86/include/asm/asm_defns.h @@ -167,7 +167,7 @@ register unsigned long current_stack_pointer asm("rsp"); #endif #define CPUINFO_FEATURE_OFFSET(feature) \ - (CPUINFO_features + (cpufeat_word(feature) * 4)) + (CPUINFO_X86_features + (cpufeat_word(feature) * 4)) #else diff --git a/xen/arch/x86/include/asm/boot-helpers.h b/xen/arch/x86/include/asm/boot-helpers.h new file mode 100644 index 0000000000..391e16a730 --- /dev/null +++ b/xen/arch/x86/include/asm/boot-helpers.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Declarations for helper functions compiled for both 32bit and 64bit. + * + * The 32bit forms are only used from assembly, so no declarations are + * provided. + */ +#ifndef X86_BOOT_HELPERS_H +#define X86_BOOT_HELPERS_H + +void reloc_trampoline64(void); + +#endif /* X86_BOOT_HELPERS_H */ diff --git a/xen/arch/x86/include/asm/bootinfo.h b/xen/arch/x86/include/asm/bootinfo.h index b9c94b370d..f8b4229130 100644 --- a/xen/arch/x86/include/asm/bootinfo.h +++ b/xen/arch/x86/include/asm/bootinfo.h @@ -26,16 +26,15 @@ enum bootmod_type { }; struct boot_module { - /* Transitionary only */ - module_t *mod; - enum bootmod_type type; /* * Module State Flags: * relocated: indicates module has been relocated in memory. + * released: indicates module's pages have been freed. */ bool relocated:1; + bool released:1; /* * A boot module may need decompressing by Xen. Headroom is an estimate of @@ -60,6 +59,9 @@ struct boot_module { unsigned long headroom; paddr_t cmdline_pa; + + paddr_t start; + size_t size; }; /* diff --git a/xen/arch/x86/include/asm/intel_txt.h b/xen/arch/x86/include/asm/intel_txt.h new file mode 100644 index 0000000000..2ec1a2ed73 --- /dev/null +++ b/xen/arch/x86/include/asm/intel_txt.h @@ -0,0 +1,337 @@ +/* + * TXT configuration registers (offsets from TXT_{PUB, PRIV}_CONFIG_REGS_BASE) + */ +#define TXT_PUB_CONFIG_REGS_BASE 0xfed30000 +#define TXT_PRIV_CONFIG_REGS_BASE 0xfed20000 + +/* The same set of registers is exposed twice (with different permissions) and + * they are allocated continuously with page alignment. */ +#define NR_TXT_CONFIG_SIZE \ + (TXT_PUB_CONFIG_REGS_BASE - TXT_PRIV_CONFIG_REGS_BASE) + +/* Offsets from pub/priv config space. */ +#define TXTCR_STS 0x0000 +#define TXTCR_ESTS 0x0008 +#define TXTCR_ERRORCODE 0x0030 +#define TXTCR_CMD_RESET 0x0038 +#define TXTCR_CMD_CLOSE_PRIVATE 0x0048 +#define TXTCR_DIDVID 0x0110 +#define TXTCR_VER_EMIF 0x0200 +#define TXTCR_CMD_UNLOCK_MEM_CONFIG 0x0218 +#define TXTCR_SINIT_BASE 0x0270 +#define TXTCR_SINIT_SIZE 0x0278 +#define TXTCR_MLE_JOIN 0x0290 +#define TXTCR_HEAP_BASE 0x0300 +#define TXTCR_HEAP_SIZE 0x0308 +#define TXTCR_SCRATCHPAD 0x0378 +#define TXTCR_CMD_OPEN_LOCALITY1 0x0380 +#define TXTCR_CMD_CLOSE_LOCALITY1 0x0388 +#define TXTCR_CMD_OPEN_LOCALITY2 0x0390 +#define TXTCR_CMD_CLOSE_LOCALITY2 0x0398 +#define TXTCR_CMD_SECRETS 0x08e0 +#define TXTCR_CMD_NO_SECRETS 0x08e8 +#define TXTCR_E2STS 0x08f0 + +/* + * Secure Launch Defined Error Codes used in MLE-initiated TXT resets. + * + * TXT Specification + * Appendix I ACM Error Codes + */ +#define SLAUNCH_ERROR_GENERIC 0xc0008001 +#define SLAUNCH_ERROR_TPM_INIT 0xc0008002 +#define SLAUNCH_ERROR_TPM_INVALID_LOG20 0xc0008003 +#define SLAUNCH_ERROR_TPM_LOGGING_FAILED 0xc0008004 +#define SLAUNCH_ERROR_REGION_STRADDLE_4GB 0xc0008005 +#define SLAUNCH_ERROR_TPM_EXTEND 0xc0008006 +#define SLAUNCH_ERROR_MTRR_INV_VCNT 0xc0008007 +#define SLAUNCH_ERROR_MTRR_INV_DEF_TYPE 0xc0008008 +#define SLAUNCH_ERROR_MTRR_INV_BASE 0xc0008009 +#define SLAUNCH_ERROR_MTRR_INV_MASK 0xc000800a +#define SLAUNCH_ERROR_MSR_INV_MISC_EN 0xc000800b +#define SLAUNCH_ERROR_INV_AP_INTERRUPT 0xc000800c +#define SLAUNCH_ERROR_INTEGER_OVERFLOW 0xc000800d +#define SLAUNCH_ERROR_HEAP_WALK 0xc000800e +#define SLAUNCH_ERROR_HEAP_MAP 0xc000800f +#define SLAUNCH_ERROR_REGION_ABOVE_4GB 0xc0008010 +#define SLAUNCH_ERROR_HEAP_INVALID_DMAR 0xc0008011 +#define SLAUNCH_ERROR_HEAP_DMAR_SIZE 0xc0008012 +#define SLAUNCH_ERROR_HEAP_DMAR_MAP 0xc0008013 +#define SLAUNCH_ERROR_HI_PMR_BASE 0xc0008014 +#define SLAUNCH_ERROR_HI_PMR_SIZE 0xc0008015 +#define SLAUNCH_ERROR_LO_PMR_BASE 0xc0008016 +#define SLAUNCH_ERROR_LO_PMR_SIZE 0xc0008017 +#define SLAUNCH_ERROR_LO_PMR_MLE 0xc0008018 +#define SLAUNCH_ERROR_INITRD_TOO_BIG 0xc0008019 +#define SLAUNCH_ERROR_HEAP_ZERO_OFFSET 0xc000801a +#define SLAUNCH_ERROR_WAKE_BLOCK_TOO_SMALL 0xc000801b +#define SLAUNCH_ERROR_MLE_BUFFER_OVERLAP 0xc000801c +#define SLAUNCH_ERROR_BUFFER_BEYOND_PMR 0xc000801d +#define SLAUNCH_ERROR_OS_SINIT_BAD_VERSION 0xc000801e +#define SLAUNCH_ERROR_EVENTLOG_MAP 0xc000801f +#define SLAUNCH_ERROR_TPM_NUMBER_ALGS 0xc0008020 +#define SLAUNCH_ERROR_TPM_UNKNOWN_DIGEST 0xc0008021 +#define SLAUNCH_ERROR_TPM_INVALID_EVENT 0xc0008022 + +#define SLAUNCH_BOOTLOADER_MAGIC 0x4c534254 + +#define TXT_AP_BOOT_CS 0x0030 +#define TXT_AP_BOOT_DS 0x0038 + +#ifndef __ASSEMBLY__ + +extern char txt_ap_entry[]; +extern uint32_t trampoline_gdt[]; + +/* We need to differentiate between pre- and post paging enabled. */ +#ifdef __XEN_CONFIG_H__ +#include +#define _txt(x) _p(x) +#else +#include +#include // __va() +#define _txt(x) __va(x) +#endif + +/* + * Always use private space as some of registers are either read-only or not + * present in public space. + */ +static inline uint64_t read_txt_reg(int reg_no) +{ + volatile uint64_t *reg = _txt(TXT_PRIV_CONFIG_REGS_BASE + reg_no); + return *reg; +} + +static inline void write_txt_reg(int reg_no, uint64_t val) +{ + volatile uint64_t *reg = _txt(TXT_PRIV_CONFIG_REGS_BASE + reg_no); + *reg = val; + /* This serves as TXT register barrier */ + (void)read_txt_reg(TXTCR_ESTS); +} + +static inline void txt_reset(uint32_t error) +{ + write_txt_reg(TXTCR_ERRORCODE, error); + write_txt_reg(TXTCR_CMD_NO_SECRETS, 1); + write_txt_reg(TXTCR_CMD_UNLOCK_MEM_CONFIG, 1); + write_txt_reg(TXTCR_CMD_RESET, 1); + while (1); +} + +/* + * Secure Launch defined OS/MLE TXT Heap table + */ +struct txt_os_mle_data { + uint32_t version; + uint32_t boot_params_addr; + uint32_t slrt; + uint32_t txt_info; + uint32_t ap_wake_block; + uint32_t ap_wake_block_size; + uint8_t mle_scratch[64]; +} __packed; + +/* + * TXT specification defined BIOS data TXT Heap table + */ +struct txt_bios_data { + uint32_t version; /* Currently 5 for TPM 1.2 and 6 for TPM 2.0 */ + uint32_t bios_sinit_size; + uint64_t reserved1; + uint64_t reserved2; + uint32_t num_logical_procs; + /* Versions >= 3 && < 5 */ + uint32_t sinit_flags; + /* Versions >= 5 with updates in version 6 */ + uint32_t mle_flags; + /* Versions >= 4 */ + /* Ext Data Elements */ +} __packed; + +/* + * TXT specification defined OS/SINIT TXT Heap table + */ +struct txt_os_sinit_data { + uint32_t version; /* Currently 6 for TPM 1.2 and 7 for TPM 2.0 */ + uint32_t flags; /* Reserved in version 6 */ + uint64_t mle_ptab; + uint64_t mle_size; + uint64_t mle_hdr_base; + uint64_t vtd_pmr_lo_base; + uint64_t vtd_pmr_lo_size; + uint64_t vtd_pmr_hi_base; + uint64_t vtd_pmr_hi_size; + uint64_t lcp_po_base; + uint64_t lcp_po_size; + uint32_t capabilities; + /* Version = 5 */ + uint64_t efi_rsdt_ptr; /* RSD*P* in versions >= 6 */ + /* Versions >= 6 */ + /* Ext Data Elements */ +} __packed; + +/* + * TXT specification defined SINIT/MLE TXT Heap table + */ +struct txt_sinit_mle_data { + uint32_t version; /* Current values are 6 through 9 */ + /* Versions <= 8, fields until lcp_policy_control must be 0 for >= 9 */ + uint8_t bios_acm_id[20]; + uint32_t edx_senter_flags; + uint64_t mseg_valid; + uint8_t sinit_hash[20]; + uint8_t mle_hash[20]; + uint8_t stm_hash[20]; + uint8_t lcp_policy_hash[20]; + uint32_t lcp_policy_control; + /* Versions >= 7 */ + uint32_t rlp_wakeup_addr; + uint32_t reserved; + uint32_t num_of_sinit_mdrs; + uint32_t sinit_mdrs_table_offset; + uint32_t sinit_vtd_dmar_table_size; + uint32_t sinit_vtd_dmar_table_offset; + /* Versions >= 8 */ + uint32_t processor_scrtm_status; + /* Versions >= 9 */ + /* Ext Data Elements */ +} __packed; + +/* Types of extended data. */ +#define TXT_HEAP_EXTDATA_TYPE_END 0 +#define TXT_HEAP_EXTDATA_TYPE_BIOS_SPEC_VER 1 +#define TXT_HEAP_EXTDATA_TYPE_ACM 2 +#define TXT_HEAP_EXTDATA_TYPE_STM 3 +#define TXT_HEAP_EXTDATA_TYPE_CUSTOM 4 +#define TXT_HEAP_EXTDATA_TYPE_MADT 6 +#define TXT_HEAP_EXTDATA_TYPE_EVENT_LOG_POINTER2_1 8 +#define TXT_HEAP_EXTDATA_TYPE_MCFG 9 +#define TXT_HEAP_EXTDATA_TYPE_TPR_REQ 13 +#define TXT_HEAP_EXTDATA_TYPE_DTPR 14 +#define TXT_HEAP_EXTDATA_TYPE_CEDT 15 + +/* + * Self-describing data structure that is used for extensions to TXT heap + * tables. + */ +struct txt_ext_data_element { + uint32_t type; /* One of TXT_HEAP_EXTDATA_TYPE_*. */ + uint32_t size; + uint8_t data[0]; /* size bytes. */ +} __packed; + +/* + * Extended data describing TPM 2.0 log. + */ +struct heap_event_log_pointer_element2_1 { + uint64_t physical_address; + uint32_t allocated_event_container_size; + uint32_t first_record_offset; + uint32_t next_record_offset; +} __packed; + +/* + * Functions to extract data from the Intel TXT Heap Memory. The layout + * of the heap is as follows: + * +------------------------------------+ + * | Size of Bios Data table (uint64_t) | + * +------------------------------------+ + * | Bios Data table | + * +------------------------------------+ + * | Size of OS MLE table (uint64_t) | + * +------------------------------------+ + * | OS MLE table | + * +-------------------------------- + + * | Size of OS SINIT table (uint64_t) | + * +------------------------------------+ + * | OS SINIT table | + * +------------------------------------+ + * | Size of SINIT MLE table (uint64_t) | + * +------------------------------------+ + * | SINIT MLE table | + * +------------------------------------+ + * + * NOTE: the table size fields include the 8 byte size field itself. + */ +static inline uint64_t txt_bios_data_size(void *heap) +{ + return *((uint64_t *)heap) - sizeof(uint64_t); +} + +static inline void *txt_bios_data_start(void *heap) +{ + return heap + sizeof(uint64_t); +} + +static inline uint64_t txt_os_mle_data_size(void *heap) +{ + return *((uint64_t *)(txt_bios_data_start(heap) + + txt_bios_data_size(heap))) - + sizeof(uint64_t); +} + +static inline void *txt_os_mle_data_start(void *heap) +{ + return txt_bios_data_start(heap) + txt_bios_data_size(heap) + + sizeof(uint64_t); +} + +static inline uint64_t txt_os_sinit_data_size(void *heap) +{ + return *((uint64_t *)(txt_os_mle_data_start(heap) + + txt_os_mle_data_size(heap))) - + sizeof(uint64_t); +} + +static inline void *txt_os_sinit_data_start(void *heap) +{ + return txt_os_mle_data_start(heap) + txt_os_mle_data_size(heap) + + sizeof(uint64_t); +} + +static inline uint64_t txt_sinit_mle_data_size(void *heap) +{ + return *((uint64_t *)(txt_os_sinit_data_start(heap) + + txt_os_sinit_data_size(heap))) - + sizeof(uint64_t); +} + +static inline void *txt_sinit_mle_data_start(void *heap) +{ + return txt_os_sinit_data_start(heap) + txt_os_sinit_data_size(heap) + + sizeof(uint64_t); +} + +static inline int is_in_pmr(struct txt_os_sinit_data *os_sinit, uint64_t base, + uint32_t size, int check_high) +{ + /* Check for size overflow. */ + if ( base + size < base ) + txt_reset(SLAUNCH_ERROR_INTEGER_OVERFLOW); + + /* Low range always starts at 0, so its size is also end address. */ + if ( base >= os_sinit->vtd_pmr_lo_base && + base + size <= os_sinit->vtd_pmr_lo_size ) + return 1; + + if ( check_high && os_sinit->vtd_pmr_hi_size != 0 ) + { + if ( os_sinit->vtd_pmr_hi_base + os_sinit->vtd_pmr_hi_size < + os_sinit->vtd_pmr_hi_size ) + txt_reset(SLAUNCH_ERROR_INTEGER_OVERFLOW); + if ( base >= os_sinit->vtd_pmr_hi_base && + base + size <= os_sinit->vtd_pmr_hi_base + + os_sinit->vtd_pmr_hi_size ) + return 1; + } + + return 0; +} + +extern void map_txt_mem_regions(void); +extern void protect_txt_mem_regions(void); +extern void txt_restore_mtrrs(bool e820_verbose); + +#endif /* __ASSEMBLY__ */ diff --git a/xen/arch/x86/include/asm/mm.h b/xen/arch/x86/include/asm/mm.h index 6c7e66ee21..d4bf9faec8 100644 --- a/xen/arch/x86/include/asm/mm.h +++ b/xen/arch/x86/include/asm/mm.h @@ -106,6 +106,9 @@ #define _PGC_need_scrub _PGC_allocated #define PGC_need_scrub PGC_allocated +/* How much of the directmap is prebuilt at compile time. */ +#define PREBUILT_MAP_LIMIT (1 << L2_PAGETABLE_SHIFT) + #ifndef CONFIG_BIGMEM /* * This definition is solely for the use in struct page_info (and diff --git a/xen/arch/x86/include/asm/msi.h b/xen/arch/x86/include/asm/msi.h index 748bc3cd6d..63adb19820 100644 --- a/xen/arch/x86/include/asm/msi.h +++ b/xen/arch/x86/include/asm/msi.h @@ -147,33 +147,26 @@ int msi_free_irq(struct msi_desc *entry); */ #define NR_HP_RESERVED_VECTORS 20 -#define msi_control_reg(base) (base + PCI_MSI_FLAGS) -#define msi_lower_address_reg(base) (base + PCI_MSI_ADDRESS_LO) -#define msi_upper_address_reg(base) (base + PCI_MSI_ADDRESS_HI) +#define msi_control_reg(base) ((base) + PCI_MSI_FLAGS) +#define msi_lower_address_reg(base) ((base) + PCI_MSI_ADDRESS_LO) +#define msi_upper_address_reg(base) ((base) + PCI_MSI_ADDRESS_HI) #define msi_data_reg(base, is64bit) \ - ( (is64bit == 1) ? base+PCI_MSI_DATA_64 : base+PCI_MSI_DATA_32 ) + ((base) + ((is64bit) ? PCI_MSI_DATA_64 : PCI_MSI_DATA_32)) #define msi_mask_bits_reg(base, is64bit) \ - ( (is64bit == 1) ? base+PCI_MSI_MASK_BIT : base+PCI_MSI_MASK_BIT-4) + ((base) + PCI_MSI_MASK_BIT - ((is64bit) ? 0 : 4)) #define msi_pending_bits_reg(base, is64bit) \ ((base) + PCI_MSI_MASK_BIT + ((is64bit) ? 4 : 0)) -#define msi_disable(control) control &= ~PCI_MSI_FLAGS_ENABLE #define multi_msi_capable(control) \ - (1 << ((control & PCI_MSI_FLAGS_QMASK) >> 1)) + (1U << MASK_EXTR(control, PCI_MSI_FLAGS_QMASK)) #define multi_msi_enable(control, num) \ - control |= (((fls(num) - 1) << 4) & PCI_MSI_FLAGS_QSIZE); -#define is_64bit_address(control) (!!(control & PCI_MSI_FLAGS_64BIT)) -#define is_mask_bit_support(control) (!!(control & PCI_MSI_FLAGS_MASKBIT)) -#define msi_enable(control, num) multi_msi_enable(control, num); \ - control |= PCI_MSI_FLAGS_ENABLE - -#define msix_control_reg(base) (base + PCI_MSIX_FLAGS) -#define msix_table_offset_reg(base) (base + PCI_MSIX_TABLE) -#define msix_pba_offset_reg(base) (base + PCI_MSIX_PBA) -#define msix_enable(control) control |= PCI_MSIX_FLAGS_ENABLE -#define msix_disable(control) control &= ~PCI_MSIX_FLAGS_ENABLE -#define msix_table_size(control) ((control & PCI_MSIX_FLAGS_QSIZE)+1) -#define msix_unmask(address) (address & ~PCI_MSIX_VECTOR_BITMASK) -#define msix_mask(address) (address | PCI_MSIX_VECTOR_BITMASK) + ((control) |= MASK_INSR(fls(num) - 1, PCI_MSI_FLAGS_QSIZE)) +#define is_64bit_address(control) (!!((control) & PCI_MSI_FLAGS_64BIT)) +#define is_mask_bit_support(control) (!!((control) & PCI_MSI_FLAGS_MASKBIT)) + +#define msix_control_reg(base) ((base) + PCI_MSIX_FLAGS) +#define msix_table_offset_reg(base) ((base) + PCI_MSIX_TABLE) +#define msix_pba_offset_reg(base) ((base) + PCI_MSIX_PBA) +#define msix_table_size(control) (((control) & PCI_MSIX_FLAGS_QSIZE) + 1) /* * MSI Defined Data Structures diff --git a/xen/arch/x86/include/asm/msr-index.h b/xen/arch/x86/include/asm/msr-index.h index 9cdb5b2625..5a153a6b84 100644 --- a/xen/arch/x86/include/asm/msr-index.h +++ b/xen/arch/x86/include/asm/msr-index.h @@ -169,6 +169,9 @@ #define MSR_X2APIC_FIRST 0x00000800 #define MSR_X2APIC_LAST 0x000008ff +/* MSR offset can be obtained by shifting MMIO offset this number of bits to the right. */ +#define MSR_X2APIC_SHIFT 4 + #define MSR_X2APIC_TPR 0x00000808 #define MSR_X2APIC_PPR 0x0000080a #define MSR_X2APIC_EOI 0x0000080b diff --git a/xen/arch/x86/include/asm/processor.h b/xen/arch/x86/include/asm/processor.h index 8776512122..1dce9c5a13 100644 --- a/xen/arch/x86/include/asm/processor.h +++ b/xen/arch/x86/include/asm/processor.h @@ -91,6 +91,8 @@ struct x86_cpu_id { extern struct cpuinfo_x86 cpu_data[]; #define current_cpu_data cpu_data[smp_processor_id()] +#define cpu_physical_id(cpu) cpu_data[cpu].apicid + extern bool probe_cpuid_faulting(void); extern void ctxt_switch_levelling(const struct vcpu *next); extern void (*ctxt_switch_masking)(const struct vcpu *next); @@ -520,6 +522,7 @@ void set_in_mcu_opt_ctrl(uint32_t mask, uint32_t val); enum ap_boot_method { AP_BOOT_NORMAL, AP_BOOT_SKINIT, + AP_BOOT_TXT, }; extern enum ap_boot_method ap_boot_method; diff --git a/xen/arch/x86/include/asm/setup.h b/xen/arch/x86/include/asm/setup.h index 8a415087e9..7041a2a435 100644 --- a/xen/arch/x86/include/asm/setup.h +++ b/xen/arch/x86/include/asm/setup.h @@ -13,7 +13,7 @@ extern char __2M_rwdata_start[], __2M_rwdata_end[]; extern unsigned long xenheap_initial_phys_start; extern uint64_t boot_tsc_stamp; -extern void *stack_start; +extern void *const stack_start; extern unsigned int multiboot_ptr; void early_cpu_init(bool verbose); @@ -34,13 +34,14 @@ void setup_io_bitmap(struct domain *d); extern struct boot_info xen_boot_info; unsigned long initial_images_nrpages(nodeid_t node); -void discard_initial_images(void); +void free_boot_modules(void); struct boot_module; void *bootstrap_map_bm(const struct boot_module *bm); -void *bootstrap_map(const module_t *mod); void bootstrap_unmap(void); +void release_boot_module(struct boot_module *bm); + struct rangeset; int remove_xen_ranges(struct rangeset *r); diff --git a/xen/arch/x86/include/asm/slaunch.h b/xen/arch/x86/include/asm/slaunch.h new file mode 100644 index 0000000000..b8a73a5ea6 --- /dev/null +++ b/xen/arch/x86/include/asm/slaunch.h @@ -0,0 +1,61 @@ +#ifndef _ASM_X86_SLAUNCH_H_ +#define _ASM_X86_SLAUNCH_H_ + +#include +#include + +#define DRTM_LOC 2 +#define DRTM_CODE_PCR 17 +#define DRTM_DATA_PCR 18 + +/* + * Secure Launch event log entry types. The TXT specification defines the + * base event value as 0x400 for DRTM values. + * + * Using the same values for AMD SKINIT. + */ +#define TXT_EVTYPE_BASE 0x400 +#define DLE_EVTYPE_SLAUNCH (TXT_EVTYPE_BASE + 0x102) +#define DLE_EVTYPE_SLAUNCH_START (TXT_EVTYPE_BASE + 0x103) +#define DLE_EVTYPE_SLAUNCH_END (TXT_EVTYPE_BASE + 0x104) + +extern bool slaunch_active; +extern uint32_t slaunch_slrt; /* physical address */ + +/* evt_log is a physical address and the caller must map it to virtual, if + * needed. */ +static inline void find_evt_log(struct slr_table *slrt, void **evt_log, + uint32_t *evt_log_size) +{ + struct slr_entry_log_info *log_info = + (void *)slr_next_entry_by_tag(slrt, NULL, SLR_ENTRY_LOG_INFO); + + if ( log_info != NULL ) + { + *evt_log = _p(log_info->addr); + *evt_log_size = log_info->size; + } + else + { + *evt_log = NULL; + *evt_log_size = 0; + } +} + +void map_slaunch_mem_regions(void); + +void protect_slaunch_mem_regions(void); + +/* + * This helper function is used to map memory using L2 page tables by aligning + * mapped regions to 2MB. This way page allocator (which at this point isn't + * yet initialized) isn't needed for creating new L1 mappings. The function + * also checks and skips memory already mapped by the prebuilt tables. + * + * There is no unmap_l2() because the function is meant to be used for code that + * accesses TXT registers and TXT heap soon after which Xen rebuilds memory + * maps, effectively dropping all existing mappings. + */ +extern int map_l2(unsigned long paddr, unsigned long size); + +#endif /* _ASM_X86_SLAUNCH_H_ */ diff --git a/xen/arch/x86/include/asm/smp.h b/xen/arch/x86/include/asm/smp.h index c8c7960134..14ce3afaf6 100644 --- a/xen/arch/x86/include/asm/smp.h +++ b/xen/arch/x86/include/asm/smp.h @@ -31,6 +31,7 @@ DECLARE_PER_CPU(cpumask_var_t, send_ipi_cpumask); extern bool park_offline_cpus; void smp_send_nmi_allbutself(void); +void smp_send_init_sipi_sipi_allbutself(void); void send_IPI_mask(const cpumask_t *mask, int vector); void send_IPI_self(int vector); @@ -39,10 +40,6 @@ extern void (*mtrr_hook) (void); extern void zap_low_mappings(void); -extern u32 x86_cpu_to_apicid[]; - -#define cpu_physical_id(cpu) x86_cpu_to_apicid[cpu] - extern void cpu_exit_clear(unsigned int cpu); extern void cpu_uninit(unsigned int cpu); int cpu_add(uint32_t apic_id, uint32_t acpi_id, uint32_t pxm); diff --git a/xen/arch/x86/include/asm/tpm.h b/xen/arch/x86/include/asm/tpm.h new file mode 100644 index 0000000000..3a0b291358 --- /dev/null +++ b/xen/arch/x86/include/asm/tpm.h @@ -0,0 +1,23 @@ +#ifndef _ASM_X86_TPM_H_ +#define _ASM_X86_TPM_H_ + +#include +#include + +#define TPM_TIS_BASE 0xFED40000 +#define TPM_TIS_SIZE 0x00010000 + +void tpm_hash_extend(unsigned loc, unsigned pcr, uint8_t *buf, unsigned size, + uint32_t type, uint8_t *log_data, unsigned log_data_size); + +/* Measures essential parts of SLR table before making use of them. */ +void tpm_measure_slrt(void); + +/* Takes measurements of DRTM policy entries except for MBI and SLRT which + * should have been measured by the time this is called. Also performs sanity + * checks of the policy and panics on failure. In particular, the function + * verifies that DRTM is consistent with modules obtained from MultibootInfo + * (MBI) and written to struct boot_info in setup.c. */ +void tpm_process_drtm_policy(const struct boot_info *bi); + +#endif /* _ASM_X86_TPM_H_ */ diff --git a/xen/arch/x86/intel_txt.c b/xen/arch/x86/intel_txt.c new file mode 100644 index 0000000000..cc9a6d01b0 --- /dev/null +++ b/xen/arch/x86/intel_txt.c @@ -0,0 +1,118 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static uint64_t __initdata txt_heap_base, txt_heap_size; + +void __init map_txt_mem_regions(void) +{ + map_l2(TXT_PRIV_CONFIG_REGS_BASE, NR_TXT_CONFIG_SIZE); + + txt_heap_base = read_txt_reg(TXTCR_HEAP_BASE); + BUG_ON(txt_heap_base == 0); + + txt_heap_size = read_txt_reg(TXTCR_HEAP_SIZE); + BUG_ON(txt_heap_size == 0); + + map_l2(txt_heap_base, txt_heap_size); +} + +void __init protect_txt_mem_regions(void) +{ + int rc; + uint64_t sinit_base, sinit_size; + + /* TXT Heap */ + BUG_ON(txt_heap_base == 0); + printk("SLAUNCH: reserving TXT heap (%#lx - %#lx)\n", txt_heap_base, + txt_heap_base + txt_heap_size); + rc = reserve_e820_ram(&e820_raw, txt_heap_base, + txt_heap_base + txt_heap_size); + BUG_ON(rc == 0); + + sinit_base = read_txt_reg(TXTCR_SINIT_BASE); + BUG_ON(sinit_base == 0); + + sinit_size = read_txt_reg(TXTCR_SINIT_SIZE); + BUG_ON(sinit_size == 0); + + /* SINIT */ + printk("SLAUNCH: reserving SINIT memory (%#lx - %#lx)\n", sinit_base, + sinit_base + sinit_size); + rc = reserve_e820_ram(&e820_raw, sinit_base, sinit_base + sinit_size); + BUG_ON(rc == 0); + + /* TXT Private Space */ + rc = e820_change_range_type(&e820_raw, TXT_PRIV_CONFIG_REGS_BASE, + TXT_PRIV_CONFIG_REGS_BASE + NR_TXT_CONFIG_SIZE, + E820_RAM, E820_UNUSABLE); + BUG_ON(rc == 0); +} + +void __init txt_restore_mtrrs(bool e820_verbose) +{ + struct txt_os_mle_data *os_mle; + struct slr_table *slrt; + struct slr_entry_intel_info *intel_info; + int os_mle_size; + uint64_t mtrr_cap, mtrr_def, base, mask; + unsigned int i; + + os_mle_size = txt_os_mle_data_size(__va(txt_heap_base)); + os_mle = txt_os_mle_data_start(__va(txt_heap_base)); + + if ( os_mle_size < sizeof(*os_mle) ) + panic("OS-MLE too small\n"); + + rdmsrl(MSR_MTRRcap, mtrr_cap); + rdmsrl(MSR_MTRRdefType, mtrr_def); + + if ( e820_verbose ) { + printk("MTRRs set previously for SINIT ACM:\n"); + printk(" MTRR cap: %"PRIx64" type: %"PRIx64"\n", mtrr_cap, mtrr_def); + + for ( i = 0; i < (uint8_t)mtrr_cap; i++ ) + { + rdmsrl(MSR_IA32_MTRR_PHYSBASE(i), base); + rdmsrl(MSR_IA32_MTRR_PHYSMASK(i), mask); + + printk(" MTRR[%d]: base %"PRIx64" mask %"PRIx64"\n", + i, base, mask); + } + } + + slrt = __va(os_mle->slrt); + intel_info = (struct slr_entry_intel_info *) + slr_next_entry_by_tag(slrt, NULL, SLR_ENTRY_INTEL_INFO); + + if ( (mtrr_cap & 0xFF) != intel_info->saved_bsp_mtrrs.mtrr_vcnt ) { + printk("Bootloader saved %ld MTRR values, but there should be %ld\n", + intel_info->saved_bsp_mtrrs.mtrr_vcnt, mtrr_cap & 0xFF); + /* Choose the smaller one to be on the safe side. */ + mtrr_cap = (mtrr_cap & 0xFF) > intel_info->saved_bsp_mtrrs.mtrr_vcnt ? + intel_info->saved_bsp_mtrrs.mtrr_vcnt : mtrr_cap; + } + + /* Restore MTRRs saved by bootloader. */ + wrmsrl(MSR_MTRRdefType, intel_info->saved_bsp_mtrrs.default_mem_type); + + for ( i = 0; i < (uint8_t)mtrr_cap; i++ ) + { + base = intel_info->saved_bsp_mtrrs.mtrr_pair[i].mtrr_physbase; + mask = intel_info->saved_bsp_mtrrs.mtrr_pair[i].mtrr_physmask; + wrmsrl(MSR_IA32_MTRR_PHYSBASE(i), base); + wrmsrl(MSR_IA32_MTRR_PHYSMASK(i), mask); + } + + if ( e820_verbose ) + printk("Restored MTRRs:\n"); /* Printed by caller, mtrr_top_of_ram(). */ +} diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c index 494c14e80f..fa21903eb2 100644 --- a/xen/arch/x86/mm.c +++ b/xen/arch/x86/mm.c @@ -5498,7 +5498,7 @@ int map_pages_to_xen( * be INVALID_MFN, since alignment is only relevant for present entries. */ #define IS_LnE_ALIGNED(v, m, n) ({ \ - mfn_t m_ = m; \ + mfn_t m_ = (m); \ \ ASSERT(!mfn_eq(m_, INVALID_MFN)); \ IS_ALIGNED(PFN_DOWN(v) | mfn_x(m_), \ diff --git a/xen/arch/x86/mpparse.c b/xen/arch/x86/mpparse.c index e74a714f50..05efc4cc0d 100644 --- a/xen/arch/x86/mpparse.c +++ b/xen/arch/x86/mpparse.c @@ -186,7 +186,7 @@ static int MP_processor_info_x(struct mpc_config_processor *m, " Processor with apicid %i ignored\n", apicid); return cpu; } - x86_cpu_to_apicid[cpu] = apicid; + cpu_physical_id(cpu) = apicid; cpumask_set_cpu(cpu, &cpu_present_map); } @@ -829,12 +829,12 @@ void mp_unregister_lapic(uint32_t apic_id, uint32_t cpu) if (!cpu || (apic_id == boot_cpu_physical_apicid)) return; - if (x86_cpu_to_apicid[cpu] != apic_id) + if (cpu_physical_id(cpu) != apic_id) return; physid_clear(apic_id, phys_cpu_present_map); - x86_cpu_to_apicid[cpu] = BAD_APICID; + cpu_physical_id(cpu) = BAD_APICID; cpumask_clear_cpu(cpu, &cpu_present_map); } diff --git a/xen/arch/x86/numa.c b/xen/arch/x86/numa.c index ae3cc7a8d0..e0b2076c13 100644 --- a/xen/arch/x86/numa.c +++ b/xen/arch/x86/numa.c @@ -54,14 +54,13 @@ bool __init arch_numa_unavailable(void) /* * Setup early cpu_to_node. * - * Populate cpu_to_node[] only if x86_cpu_to_apicid[], - * and apicid_to_node[] tables have valid entries for a CPU. - * This means we skip cpu_to_node[] initialisation for NUMA - * emulation and faking node case (when running a kernel compiled - * for NUMA on a non NUMA box), which is OK as cpu_to_node[] - * is already initialized in a round robin manner at numa_init_array, - * prior to this call, and this initialization is good enough - * for the fake NUMA cases. + * Populate cpu_to_node[] only if cpu_data[], and apicid_to_node[] + * tables have valid entries for a CPU. This means we skip + * cpu_to_node[] initialisation for NUMA emulation and faking node + * case (when running a kernel compiled for NUMA on a non NUMA box), + * which is OK as cpu_to_node[] is already initialized in a round + * robin manner at numa_init_array, prior to this call, and this + * initialization is good enough for the fake NUMA cases. */ void __init init_cpu_to_node(void) { @@ -70,7 +69,7 @@ void __init init_cpu_to_node(void) for ( i = 0; i < nr_cpu_ids; i++ ) { - u32 apicid = x86_cpu_to_apicid[i]; + u32 apicid = cpu_physical_id(i); if ( apicid == BAD_APICID ) continue; node = apicid < MAX_LOCAL_APIC ? apicid_to_node[apicid] : NUMA_NO_NODE; diff --git a/xen/arch/x86/platform_hypercall.c b/xen/arch/x86/platform_hypercall.c index 67f851237d..7dabf85330 100644 --- a/xen/arch/x86/platform_hypercall.c +++ b/xen/arch/x86/platform_hypercall.c @@ -597,7 +597,7 @@ ret_t do_platform_op( } else { - g_info->apic_id = x86_cpu_to_apicid[g_info->xen_cpuid]; + g_info->apic_id = cpu_physical_id(g_info->xen_cpuid); g_info->acpi_id = acpi_get_processor_id(g_info->xen_cpuid); ASSERT(g_info->apic_id != BAD_APICID); g_info->flags = 0; diff --git a/xen/arch/x86/pv/dom0_build.c b/xen/arch/x86/pv/dom0_build.c index 9cf4519111..f54d1da5c6 100644 --- a/xen/arch/x86/pv/dom0_build.c +++ b/xen/arch/x86/pv/dom0_build.c @@ -422,14 +422,14 @@ static int __init dom0_construct(struct boot_info *bi, struct domain *d) image = &bi->mods[i]; image_base = bootstrap_map_bm(image); - image_len = image->mod->mod_end; + image_len = image->size; image_start = image_base + image->headroom; i = first_boot_module_index(bi, BOOTMOD_RAMDISK); if ( i < bi->nr_modules ) { initrd = &bi->mods[i]; - initrd_len = initrd->mod->mod_end; + initrd_len = initrd->size; } d->max_pages = ~0U; @@ -631,7 +631,7 @@ static int __init dom0_construct(struct boot_info *bi, struct domain *d) initrd_pfn = vinitrd_start ? (vinitrd_start - v_start) >> PAGE_SHIFT : domain_tot_pages(d); - initrd_mfn = initrd->mod->mod_start; + initrd_mfn = paddr_to_pfn(initrd->start); mfn = initrd_mfn; count = PFN_UP(initrd_len); if ( d->arch.physaddr_bitsize && @@ -647,31 +647,29 @@ static int __init dom0_construct(struct boot_info *bi, struct domain *d) free_domheap_pages(page, order); page += 1UL << order; } - memcpy(page_to_virt(page), mfn_to_virt(initrd->mod->mod_start), - initrd_len); - mpt_alloc = pfn_to_paddr(initrd->mod->mod_start); - init_domheap_pages(mpt_alloc, - mpt_alloc + PAGE_ALIGN(initrd_len)); + memcpy(page_to_virt(page), __va(initrd->start), initrd_len); + /* + * The initrd was copied but the initrd variable is reused in the + * calculations below. As to not leak the memory used for the + * module free at this time. + */ + release_boot_module(initrd); initrd_mfn = mfn_x(page_to_mfn(page)); - initrd->mod->mod_start = initrd_mfn; + initrd->start = pfn_to_paddr(initrd_mfn); } else { while ( count-- ) if ( assign_pages(mfn_to_page(_mfn(mfn++)), 1, d, 0) ) BUG(); + /* + * We have mapped the initrd directly into dom0, and assigned the + * pages. Tell the boot_module handling that we've freed it, so the + * memory is left alone. + */ + initrd->released = true; } - /* - * We have either: - * - Mapped the initrd directly into dom0, or - * - Copied it and freed the module. - * - * Either way, tell discard_initial_images() to not free it a second - * time. - */ - initrd->mod->mod_end = 0; - iommu_memory_setup(d, "initrd", mfn_to_page(_mfn(initrd_mfn)), PFN_UP(initrd_len), &flush_flags); } @@ -684,7 +682,7 @@ static int __init dom0_construct(struct boot_info *bi, struct domain *d) nr_pages - domain_tot_pages(d)); if ( initrd ) { - mpt_alloc = pfn_to_paddr(initrd->mod->mod_start); + mpt_alloc = initrd->start; printk("\n Init. ramdisk: %"PRIpaddr"->%"PRIpaddr, mpt_alloc, mpt_alloc + initrd_len); } @@ -875,7 +873,7 @@ static int __init dom0_construct(struct boot_info *bi, struct domain *d) } /* Free temporary buffers. */ - discard_initial_images(); + free_boot_modules(); /* Set up start info area. */ si = (start_info_t *)vstartinfo_start; @@ -912,7 +910,7 @@ static int __init dom0_construct(struct boot_info *bi, struct domain *d) if ( pfn >= initrd_pfn ) { if ( pfn < initrd_pfn + PFN_UP(initrd_len) ) - mfn = initrd->mod->mod_start + (pfn - initrd_pfn); + mfn = paddr_to_pfn(initrd->start) + (pfn - initrd_pfn); else mfn -= PFN_UP(initrd_len); } diff --git a/xen/arch/x86/setup.c b/xen/arch/x86/setup.c index 302ff61e2b..b59b754763 100644 --- a/xen/arch/x86/setup.c +++ b/xen/arch/x86/setup.c @@ -62,6 +62,8 @@ #include #include #include +#include +#include /* opt_nosmp: If true, secondary processors are ignored. */ static bool __initdata opt_nosmp; @@ -155,8 +157,8 @@ unsigned long __read_mostly xen_phys_start; char asmlinkage __section(".init.bss.stack_aligned") __aligned(STACK_SIZE) cpu0_stack[STACK_SIZE]; -/* Used by the BSP/AP paths to find the higher half stack mapping to use. */ -void *stack_start = cpu0_stack + STACK_SIZE - sizeof(struct cpu_info); +/* Used by the BSP path to find the higher half stack mapping to use. */ +void *const stack_start = cpu0_stack + STACK_SIZE - sizeof(struct cpu_info); /* Used by the boot asm and EFI to stash the multiboot_info paddr. */ unsigned int __initdata multiboot_ptr; @@ -314,13 +316,29 @@ static struct boot_info *__init multiboot_fill_boot_info( */ for ( i = 0; i < MAX_NR_BOOTMODS && i < bi->nr_modules; i++ ) { - bi->mods[i].mod = &mods[i]; - bi->mods[i].cmdline_pa = mods[i].string; + + if ( efi_enabled(EFI_LOADER) ) + { + /* + * The EFI loader gives us modules which are in frame/size. Switch + * to address/size. + */ + bi->mods[i].start = pfn_to_paddr(mods[i].mod_start); + bi->mods[i].size = mods[i].mod_end; + } + else + { + /* + * PVH and BIOS loaders give us modules which are start/end. + * Switch to address/size. + */ + bi->mods[i].start = mods[i].mod_start; + bi->mods[i].size = mods[i].mod_end - mods[i].mod_start; + } } /* Variable 'i' should be one entry past the last module. */ - bi->mods[i].mod = &mods[bi->nr_modules]; bi->mods[i].type = BOOTMOD_XEN; return bi; @@ -336,8 +354,8 @@ unsigned long __init initial_images_nrpages(nodeid_t node) for ( nr = i = 0; i < bi->nr_modules; ++i ) { - unsigned long start = bi->mods[i].mod->mod_start; - unsigned long end = start + PFN_UP(bi->mods[i].mod->mod_end); + unsigned long start = paddr_to_pfn(bi->mods[i].start); + unsigned long end = start + PFN_UP(bi->mods[i].size); if ( end > node_start && node_end > start ) nr += min(node_end, end) - max(node_start, start); @@ -346,27 +364,27 @@ unsigned long __init initial_images_nrpages(nodeid_t node) return nr; } -void __init discard_initial_images(void) /* a.k.a. Free boot modules */ +void __init release_boot_module(struct boot_module *bm) +{ + ASSERT(!bm->released); + + init_domheap_pages(bm->start, bm->start + PAGE_ALIGN(bm->size)); + + bm->released = true; +} + +void __init free_boot_modules(void) { struct boot_info *bi = &xen_boot_info; unsigned int i; for ( i = 0; i < bi->nr_modules; ++i ) { - uint64_t start = pfn_to_paddr(bi->mods[i].mod->mod_start); - uint64_t size = bi->mods[i].mod->mod_end; - - /* - * Sometimes the initrd is mapped, rather than copied, into dom0. - * Size being 0 is how we're instructed to leave the module alone. - */ - if ( size == 0 ) + if ( bi->mods[i].released ) continue; - init_domheap_pages(start, start + PAGE_ALIGN(size)); + release_boot_module(&bi->mods[i]); } - - bi->nr_modules = 0; } static void __init init_idle_domain(void) @@ -379,7 +397,7 @@ static void __init init_idle_domain(void) void srat_detect_node(int cpu) { nodeid_t node; - u32 apicid = x86_cpu_to_apicid[cpu]; + u32 apicid = cpu_physical_id(cpu); node = apicid < MAX_LOCAL_APIC ? apicid_to_node[apicid] : NUMA_NO_NODE; if ( node == NUMA_NO_NODE ) @@ -406,7 +424,7 @@ static void __init normalise_cpu_order(void) for_each_present_cpu ( i ) { - apicid = x86_cpu_to_apicid[i]; + apicid = cpu_physical_id(i); min_diff = min_cpu = ~0u; /* @@ -417,12 +435,12 @@ static void __init normalise_cpu_order(void) j < nr_cpu_ids; j = cpumask_next(j, &cpu_present_map) ) { - diff = x86_cpu_to_apicid[j] ^ apicid; + diff = cpu_physical_id(j) ^ apicid; while ( diff & (diff-1) ) diff &= diff-1; if ( (diff < min_diff) || ((diff == min_diff) && - (x86_cpu_to_apicid[j] < x86_cpu_to_apicid[min_cpu])) ) + (cpu_physical_id(j) < cpu_physical_id(min_cpu))) ) { min_diff = diff; min_cpu = j; @@ -438,9 +456,9 @@ static void __init normalise_cpu_order(void) /* Switch the best-matching CPU with the next CPU in logical order. */ j = cpumask_next(i, &cpu_present_map); - apicid = x86_cpu_to_apicid[min_cpu]; - x86_cpu_to_apicid[min_cpu] = x86_cpu_to_apicid[j]; - x86_cpu_to_apicid[j] = apicid; + apicid = cpu_physical_id(min_cpu); + cpu_physical_id(min_cpu) = cpu_physical_id(j); + cpu_physical_id(j) = apicid; } } @@ -484,15 +502,9 @@ static void *__init bootstrap_map_addr(paddr_t start, paddr_t end) return ret; } -void *__init bootstrap_map(const module_t *mod) -{ - return bootstrap_map_addr(pfn_to_paddr(mod->mod_start), - pfn_to_paddr(mod->mod_start) + mod->mod_end); -} - void *__init bootstrap_map_bm(const struct boot_module *bm) { - return bootstrap_map(bm->mod); + return bootstrap_map_addr(bm->start, bm->start + bm->size); } void __init bootstrap_unmap(void) @@ -670,8 +682,8 @@ static uint64_t __init consider_modules( for ( i = 0; i < nr_mods ; ++i ) { - uint64_t start = pfn_to_paddr(mods[i].mod->mod_start); - uint64_t end = start + PAGE_ALIGN(mods[i].mod->mod_end); + uint64_t start = mods[i].start; + uint64_t end = start + PAGE_ALIGN(mods[i].size); if ( i == this_mod ) continue; @@ -1038,9 +1050,6 @@ static struct domain *__init create_dom0(struct boot_info *bi) return d; } -/* How much of the directmap is prebuilt at compile time. */ -#define PREBUILT_MAP_LIMIT (1 << L2_PAGETABLE_SHIFT) - void asmlinkage __init noreturn __start_xen(void) { const char *memmap_type = NULL; @@ -1376,6 +1385,19 @@ void asmlinkage __init noreturn __start_xen(void) #endif } + if ( slaunch_active ) + { + /* Prepare for accesses to essential data structures setup by boot + * environment. */ + map_slaunch_mem_regions(); + + /* Measure SLRT here because it gets used by init_e820(), the rest is + * measured below by tpm_process_drtm_policy(). */ + tpm_measure_slrt(); + + protect_slaunch_mem_regions(); + } + /* Sanitise the raw E820 map to produce a final clean version. */ max_page = raw_max_page = init_e820(memmap_type, &e820_raw); @@ -1394,6 +1416,14 @@ void asmlinkage __init noreturn __start_xen(void) /* Create a temporary copy of the E820 map. */ memcpy(&boot_e820, &e820, sizeof(e820)); + /* + * Process all yet unmeasured DRTM entries after E820 initialization to not + * do this while memory is uncached (too slow). This must also happen before + * modules are relocated or used. + */ + if ( slaunch_active ) + tpm_process_drtm_policy(bi); + /* Early kexec reservation (explicit static start address). */ nr_pages = 0; for ( i = 0; i < e820.nr_map; i++ ) @@ -1402,13 +1432,9 @@ void asmlinkage __init noreturn __start_xen(void) set_kexec_crash_area_size((u64)nr_pages << PAGE_SHIFT); kexec_reserve_area(); - for ( i = 0; !efi_enabled(EFI_LOADER) && i < bi->nr_modules; i++ ) - { - if ( bi->mods[i].mod->mod_start & (PAGE_SIZE - 1) ) + for ( i = 0; i < bi->nr_modules; i++ ) + if ( bi->mods[i].start & (PAGE_SIZE - 1) ) panic("Bootloader didn't honor module alignment request\n"); - bi->mods[i].mod->mod_end -= bi->mods[i].mod->mod_start; - bi->mods[i].mod->mod_start >>= PAGE_SHIFT; - } /* * TODO: load ucode earlier once multiboot modules become accessible @@ -1427,13 +1453,12 @@ void asmlinkage __init noreturn __start_xen(void) * respective reserve_e820_ram() invocation below. No need to * query efi_boot_mem_unused() here, though. */ - xen->mod->mod_start = virt_to_mfn(_stext); - xen->mod->mod_end = __2M_rwdata_end - _stext; + xen->start = virt_to_maddr(_stext); + xen->size = __2M_rwdata_end - _stext; } bi->mods[0].headroom = - bzimage_headroom(bootstrap_map_bm(&bi->mods[0]), - bi->mods[0].mod->mod_end); + bzimage_headroom(bootstrap_map_bm(&bi->mods[0]), bi->mods[0].size); bootstrap_unmap(); #ifndef highmem_start @@ -1514,7 +1539,7 @@ void asmlinkage __init noreturn __start_xen(void) for ( j = bi->nr_modules - 1; j >= 0; j-- ) { struct boot_module *bm = &bi->mods[j]; - unsigned long size = PAGE_ALIGN(bm->headroom + bm->mod->mod_end); + unsigned long size = PAGE_ALIGN(bm->headroom + bm->size); if ( bm->relocated ) continue; @@ -1526,14 +1551,11 @@ void asmlinkage __init noreturn __start_xen(void) if ( highmem_start && end > highmem_start ) continue; - if ( s < end && - (bm->headroom || - ((end - size) >> PAGE_SHIFT) > bm->mod->mod_start) ) + if ( s < end && (bm->headroom || (end - size) > bm->start) ) { - move_memory(end - size + bm->headroom, - pfn_to_paddr(bm->mod->mod_start), bm->mod->mod_end); - bm->mod->mod_start = (end - size) >> PAGE_SHIFT; - bm->mod->mod_end += bm->headroom; + move_memory(end - size + bm->headroom, bm->start, bm->size); + bm->start = (end - size); + bm->size += bm->headroom; bm->relocated = true; } } @@ -1564,10 +1586,9 @@ void asmlinkage __init noreturn __start_xen(void) panic("Not enough memory to relocate the dom0 kernel image\n"); for ( i = 0; i < bi->nr_modules; ++i ) { - const struct boot_module *bm = &bi->mods[i]; - uint64_t s = pfn_to_paddr(bm->mod->mod_start); + uint64_t s = bi->mods[i].start, l = bi->mods[i].size; - reserve_e820_ram(&boot_e820, s, s + PAGE_ALIGN(bm->mod->mod_end)); + reserve_e820_ram(&boot_e820, s, s + PAGE_ALIGN(l)); } if ( !xen_phys_start ) @@ -1645,8 +1666,7 @@ void asmlinkage __init noreturn __start_xen(void) map_e = boot_e820.map[j].addr + boot_e820.map[j].size; for ( j = 0; j < bi->nr_modules; ++j ) { - uint64_t end = pfn_to_paddr(bi->mods[j].mod->mod_start) + - bi->mods[j].mod->mod_end; + uint64_t end = bi->mods[j].start + bi->mods[j].size; if ( map_e < end ) map_e = end; @@ -1720,13 +1740,11 @@ void asmlinkage __init noreturn __start_xen(void) for ( i = 0; i < bi->nr_modules; ++i ) { - const struct boot_module *bm = &bi->mods[i]; + unsigned long s = bi->mods[i].start, l = bi->mods[i].size; - set_pdx_range(bm->mod->mod_start, - bm->mod->mod_start + PFN_UP(bm->mod->mod_end)); - map_pages_to_xen((unsigned long)mfn_to_virt(bm->mod->mod_start), - _mfn(bm->mod->mod_start), - PFN_UP(bm->mod->mod_end), PAGE_HYPERVISOR); + set_pdx_range(paddr_to_pfn(s), paddr_to_pfn(s + l) + 1); + map_pages_to_xen((unsigned long)maddr_to_virt(s), maddr_to_mfn(s), + PFN_UP(l), PAGE_HYPERVISOR); } #ifdef CONFIG_KEXEC @@ -2053,6 +2071,7 @@ void asmlinkage __init noreturn __start_xen(void) */ if ( !pv_shim ) { + /* Separate loop to make parallel AP bringup possible. */ for_each_present_cpu ( i ) { /* Set up cpu_to_node[]. */ @@ -2060,6 +2079,14 @@ void asmlinkage __init noreturn __start_xen(void) /* Set up node_to_cpumask based on cpu_to_node[]. */ numa_add_cpu(i); + if ( stack_base[i] == NULL ) + stack_base[i] = cpu_alloc_stack(i); + } + + smp_send_init_sipi_sipi_allbutself(); + + for_each_present_cpu ( i ) + { if ( (park_offline_cpus || num_online_cpus() < max_cpus) && !cpu_online(i) ) { diff --git a/xen/arch/x86/shutdown.c b/xen/arch/x86/shutdown.c index 902076cf67..3ebac80257 100644 --- a/xen/arch/x86/shutdown.c +++ b/xen/arch/x86/shutdown.c @@ -549,9 +549,23 @@ void machine_restart(unsigned int delay_millisecs) /* Ensure we are the boot CPU. */ if ( get_apic_id() != boot_cpu_physical_apicid ) { - /* Send IPI to the boot CPU (logical cpu 0). */ - on_selected_cpus(cpumask_of(0), __machine_restart, - &delay_millisecs, 0); + /* + * Send IPI to the boot CPU (logical cpu 0). + * + * If multiple CPUs called machine_restart() before actual restart + * took place, but after boot CPU declared itself not online, ASSERT + * in on_selected_cpus() will fail. Few calls later we would end up + * here again, with another frame on call stack for new exception. + * To protect against running out of stack, check if boot CPU is + * online. + * + * Note this is not an atomic operation, so it is possible for + * on_selected_cpus() to be called once after boot CPU is offline + * before we hit halt() below. + */ + if ( cpu_online(0) ) + on_selected_cpus(cpumask_of(0), __machine_restart, + &delay_millisecs, 0); for ( ; ; ) halt(); } diff --git a/xen/arch/x86/slaunch.c b/xen/arch/x86/slaunch.c new file mode 100644 index 0000000000..393c1a25b5 --- /dev/null +++ b/xen/arch/x86/slaunch.c @@ -0,0 +1,327 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* SLB is 64k, 64k-aligned */ +#define SKINIT_SLB_SIZE 0x10000 +#define SKINIT_SLB_ALIGN 0x10000 + +bool __initdata slaunch_active; +uint32_t __initdata slaunch_slrt; + +static void __maybe_unused compile_time_checks(void) +{ + BUILD_BUG_ON(sizeof(slaunch_active) != 1); +} + +int __init map_l2(unsigned long paddr, unsigned long size) +{ + unsigned long aligned_paddr = paddr & ~((1ULL << L2_PAGETABLE_SHIFT) - 1); + unsigned long pages = ((paddr + size) - aligned_paddr); + pages = ROUNDUP(pages, 1ULL << L2_PAGETABLE_SHIFT) >> PAGE_SHIFT; + + if ( (aligned_paddr + pages * PAGE_SIZE) <= PREBUILT_MAP_LIMIT ) + return 0; + + if ( aligned_paddr < PREBUILT_MAP_LIMIT ) + { + pages -= (PREBUILT_MAP_LIMIT - aligned_paddr) >> PAGE_SHIFT; + aligned_paddr = PREBUILT_MAP_LIMIT; + } + + return map_pages_to_xen((unsigned long)__va(aligned_paddr), + maddr_to_mfn(aligned_paddr), + pages, PAGE_HYPERVISOR); +} + +static uint32_t get_slb_start(void) +{ + /* The runtime computation relies on size being a power of 2 and equal to + * alignment. Make sure these assumptions hold. */ + BUILD_BUG_ON(SKINIT_SLB_SIZE != SKINIT_SLB_ALIGN); + BUILD_BUG_ON(SKINIT_SLB_SIZE == 0); + BUILD_BUG_ON((SKINIT_SLB_SIZE & (SKINIT_SLB_SIZE - 1)) != 0); + + /* Rounding any address within SLB down to alignment gives SLB base and + * SLRT is inside SLB on AMD. */ + return slaunch_slrt & ~(SKINIT_SLB_SIZE - 1); +} + +void __init map_slaunch_mem_regions(void) +{ + void *evt_log_addr; + uint32_t evt_log_size; + + map_l2(TPM_TIS_BASE, TPM_TIS_SIZE); + + /* Vendor-specific part. It may include contain slaunch_slrt. */ + if ( boot_cpu_data.x86_vendor == X86_VENDOR_INTEL ) + { + map_txt_mem_regions(); + } + else if ( boot_cpu_data.x86_vendor == X86_VENDOR_AMD ) + { + map_l2(get_slb_start(), SKINIT_SLB_SIZE); + } + + find_evt_log(__va(slaunch_slrt), &evt_log_addr, &evt_log_size); + if ( evt_log_addr != NULL ) + map_l2((unsigned long)evt_log_addr, evt_log_size); +} + +void __init protect_slaunch_mem_regions(void) +{ + int rc; + + void *evt_log_addr; + uint32_t evt_log_size; + + find_evt_log(__va(slaunch_slrt), &evt_log_addr, &evt_log_size); + if ( evt_log_addr != NULL ) + { + printk("SLAUNCH: reserving event log (%#lx - %#lx)\n", + (uint64_t)evt_log_addr, + (uint64_t)evt_log_addr + evt_log_size); + rc = reserve_e820_ram(&e820_raw, (uint64_t)evt_log_addr, + (uint64_t)evt_log_addr + evt_log_size); + BUG_ON(rc == 0); + } + + /* Vendor-specific part. */ + if ( boot_cpu_data.x86_vendor == X86_VENDOR_INTEL ) + { + protect_txt_mem_regions(); + } + else if ( boot_cpu_data.x86_vendor == X86_VENDOR_AMD ) + { + uint64_t slb_start = get_slb_start(); + uint64_t slb_end = slb_start + SKINIT_SLB_SIZE; + printk("SLAUNCH: reserving SLB (%#lx - %#lx)\n", slb_start, slb_end); + e820_change_range_type(&e820_raw, slb_start, slb_end, + E820_RAM, E820_RESERVED); + } +} + +static struct slr_table *slr_get_table(void) +{ + bool intel_cpu = (boot_cpu_data.x86_vendor == X86_VENDOR_INTEL); + uint16_t slrt_architecture = intel_cpu ? SLR_INTEL_TXT : SLR_AMD_SKINIT; + + struct slr_table *slrt = __va(slaunch_slrt); + + map_l2(slaunch_slrt, PAGE_SIZE); + + if ( slrt->magic != SLR_TABLE_MAGIC ) + panic("SLRT has invalid magic value: %#08x!\n", slrt->magic); + /* XXX: are newer revisions allowed? */ + if ( slrt->revision != SLR_TABLE_REVISION ) + panic("SLRT is of unsupported revision: %#04x!\n", slrt->revision); + if ( slrt->architecture != slrt_architecture ) + panic("SLRT is for unexpected architecture: %#04x != %#04x!\n", + slrt->architecture, slrt_architecture); + if ( slrt->size > slrt->max_size ) + panic("SLRT is larger than its max size: %#08x > %#08x!\n", + slrt->size, slrt->max_size); + + if ( slrt->size > PAGE_SIZE ) + map_l2(slaunch_slrt, slrt->size); + + return slrt; +} + +void tpm_measure_slrt(void) +{ + struct slr_table *slrt = slr_get_table(); + + if ( slrt->revision == 1 ) + { + if ( boot_cpu_data.x86_vendor == X86_VENDOR_INTEL ) + { + /* In revision one of the SLRT, only Intel info table is + * measured. */ + struct slr_entry_intel_info *intel_info = + (void *)slr_next_entry_by_tag(slrt, NULL, SLR_ENTRY_INTEL_INFO); + if ( intel_info == NULL ) + panic("SLRT is missing Intel-specific information!\n"); + + tpm_hash_extend(DRTM_LOC, DRTM_DATA_PCR, (uint8_t *)intel_info, + sizeof(*intel_info), DLE_EVTYPE_SLAUNCH, NULL, 0); + } + } + else + { + /* + * slr_get_table() checks that the revision is valid, so we must not + * get here unless the code is wrong. + */ + panic("Unhandled SLRT revision: %d!\n", slrt->revision); + } +} + +static struct slr_entry_policy *slr_get_policy(struct slr_table *slrt) +{ + struct slr_entry_policy *policy; + + policy = (struct slr_entry_policy *) + slr_next_entry_by_tag(slrt, NULL, SLR_ENTRY_DRTM_POLICY); + if (policy == NULL) + panic("SLRT is missing DRTM policy!\n"); + + /* XXX: are newer revisions allowed? */ + if ( policy->revision != SLR_POLICY_REVISION ) + panic("DRTM policy in SLRT is of unsupported revision: %#04x!\n", + slrt->revision); + + return policy; +} + +static void check_drtm_policy(struct slr_table *slrt, + struct slr_entry_policy *policy, + struct slr_policy_entry *policy_entry, + const struct boot_info *bi) +{ + uint32_t i; + uint32_t num_mod_entries; + + if ( policy->nr_entries < 2 ) + panic("DRTM policy in SLRT contains less than 2 entries (%d)!\n", + policy->nr_entries); + + /* MBI policy entry must be the first one, so that measuring order matches + * policy order. */ + if ( policy_entry[0].entity_type != SLR_ET_MULTIBOOT2_INFO ) + panic("First entry of DRTM policy in SLRT is not MBI: %#04x!\n", + policy_entry[0].entity_type); + if ( policy_entry[0].pcr != DRTM_DATA_PCR ) + panic("MBI was measured to %d instead of %d PCR!\n", DRTM_DATA_PCR, + policy_entry[0].pcr); + + /* SLRT policy entry must be the second one. */ + if ( policy_entry[1].entity_type != SLR_ET_SLRT ) + panic("Second entry of DRTM policy in SLRT is not SLRT: %#04x!\n", + policy_entry[1].entity_type); + if ( policy_entry[1].pcr != DRTM_DATA_PCR ) + panic("SLRT was measured to %d instead of %d PCR!\n", DRTM_DATA_PCR, + policy_entry[1].pcr); + if ( policy_entry[1].entity != (uint64_t)__pa(slrt) ) + panic("SLRT address (%#08lx) differes from its DRTM entry (%#08lx)\n", + __pa(slrt), policy_entry[1].entity); + + /* + * XXX: this loop uses transitionary format of struct boot_module, added in + * 7cf6e073e47 "x86/boot: introduce struct boot_module", and will have to + * be updated if that definition changes. + */ + for ( i = 0; i < bi->nr_modules; i++ ) + { + uint16_t j; + const struct boot_module *mod = &bi->mods[i]; + + if (mod->relocated || mod->released) + { + panic("Multiboot module \"%s\" (at %d) was consumed before measurement\n", + (const char *)__va(mod->cmdline_pa), i); + } + + for ( j = 2; j < policy->nr_entries; j++ ) + { + if ( policy_entry[j].entity_type != SLR_ET_MULTIBOOT2_MODULE ) + continue; + + if ( policy_entry[j].entity == mod->start && + policy_entry[j].size == mod->size ) + break; + } + + if ( j >= policy->nr_entries ) + { + panic("Couldn't find Multiboot module \"%s\" (at %d) in DRTM of Secure Launch\n", + (const char *)__va(mod->cmdline_pa), i); + } + } + + num_mod_entries = 0; + for ( i = 0; i < policy->nr_entries; i++ ) + { + if ( policy_entry[i].entity_type == SLR_ET_MULTIBOOT2_MODULE ) + num_mod_entries++; + } + + if ( bi->nr_modules != num_mod_entries ) + { + panic("Unexpected number of Multiboot modules: %d instead of %d\n", + (int)bi->nr_modules, (int)num_mod_entries); + } +} + +void tpm_process_drtm_policy(const struct boot_info *bi) +{ + struct slr_table *slrt; + struct slr_entry_policy *policy; + struct slr_policy_entry *policy_entry; + uint16_t i; + + slrt = slr_get_table(); + + policy = slr_get_policy(slrt); + policy_entry = (struct slr_policy_entry *) + ((uint8_t *)policy + sizeof(*policy)); + + check_drtm_policy(slrt, policy, policy_entry, bi); + /* MBI was measured in tpm_extend_mbi(). */ + policy_entry[0].flags |= SLR_POLICY_FLAG_MEASURED; + /* SLRT was measured in tpm_measure_slrt(). */ + policy_entry[1].flags |= SLR_POLICY_FLAG_MEASURED; + + for ( i = 2; i < policy->nr_entries; i++ ) + { + uint64_t start = policy_entry[i].entity; + uint64_t size = policy_entry[i].size; + + /* No already measured entries are expected here. */ + if ( policy_entry[i].flags & SLR_POLICY_FLAG_MEASURED ) + panic("DRTM entry at %d was measured out of order!\n", i); + + switch ( policy_entry[i].entity_type ) + { + case SLR_ET_MULTIBOOT2_INFO: + panic("Duplicated MBI entry in DRTM of Secure Launch at %d\n", i); + case SLR_ET_SLRT: + panic("Duplicated SLRT entry in DRTM of Secure Launch at %d\n", i); + + case SLR_ET_UNSPECIFIED: + case SLR_ET_BOOT_PARAMS: + case SLR_ET_SETUP_DATA: + case SLR_ET_CMDLINE: + case SLR_ET_UEFI_MEMMAP: + case SLR_ET_RAMDISK: + case SLR_ET_MULTIBOOT2_MODULE: + case SLR_ET_TXT_OS2MLE: + /* Measure this entry below. */ + break; + + case SLR_ET_UNUSED: + /* Skip this entry. */ + continue; + } + + if ( policy_entry[i].flags & SLR_POLICY_IMPLICIT_SIZE ) + panic("Unexpected implicitly-sized DRTM entry of Secure Launch at %d\n", + i); + + map_l2(start, size); + tpm_hash_extend(DRTM_LOC, policy_entry[i].pcr, __va(start), size, + DLE_EVTYPE_SLAUNCH, (uint8_t *)policy_entry[i].evt_info, + strnlen(policy_entry[i].evt_info, + TPM_EVENT_INFO_LENGTH)); + + policy_entry[i].flags |= SLR_POLICY_FLAG_MEASURED; + } +} diff --git a/xen/arch/x86/smpboot.c b/xen/arch/x86/smpboot.c index 79a79c54c3..5f8e8fd9ba 100644 --- a/xen/arch/x86/smpboot.c +++ b/xen/arch/x86/smpboot.c @@ -30,6 +30,8 @@ #include #include #include +#include +#include #include #include #include @@ -64,27 +66,30 @@ unsigned int __read_mostly nr_sockets; cpumask_t **__read_mostly socket_cpumask; static cpumask_t *secondary_socket_cpumask; -struct cpuinfo_x86 cpu_data[NR_CPUS]; - -u32 x86_cpu_to_apicid[NR_CPUS] __read_mostly = - { [0 ... NR_CPUS-1] = BAD_APICID }; +struct cpuinfo_x86 cpu_data[NR_CPUS] = + { [0 ... NR_CPUS-1] .apicid = BAD_APICID }; static int cpu_error; static enum cpu_state { CPU_STATE_DYING, /* slave -> master: I am dying */ CPU_STATE_DEAD, /* slave -> master: I am completely dead */ - CPU_STATE_INIT, /* master -> slave: Early bringup phase 1 */ - CPU_STATE_CALLOUT, /* master -> slave: Early bringup phase 2 */ + CPU_STATE_INIT, /* slave -> master: Early bringup phase 1 completed */ + CPU_STATE_CALLOUT, /* master -> slave: Start early bringup phase 2 */ CPU_STATE_CALLIN, /* slave -> master: Completed phase 2 */ CPU_STATE_ONLINE /* master -> slave: Go fully online now. */ -} cpu_state; -#define set_cpu_state(state) do { smp_mb(); cpu_state = (state); } while (0) +} cpu_state[NR_CPUS]; +#define set_cpu_state(cpu, state) do { \ + smp_mb(); \ + cpu_state[cpu] = (state); \ +} while (0) void *stack_base[NR_CPUS]; void initialize_cpu_data(unsigned int cpu) { + uint32_t apicid = cpu_physical_id(cpu); cpu_data[cpu] = boot_cpu_data; + cpu_physical_id(cpu) = apicid; } static bool smp_store_cpu_info(unsigned int id) @@ -169,23 +174,13 @@ static void synchronize_tsc_slave(unsigned int slave) static void smp_callin(void) { unsigned int cpu = smp_processor_id(); - int i, rc; - - /* Wait 2s total for startup. */ - Dprintk("Waiting for CALLOUT.\n"); - for ( i = 0; cpu_state != CPU_STATE_CALLOUT; i++ ) - { - BUG_ON(i >= 200); - cpu_relax(); - mdelay(10); - } + int rc; /* * The boot CPU has finished the init stage and is spinning on cpu_state * update until we finish. We are free to set up this CPU: first the APIC. */ Dprintk("CALLIN, before setup_local_APIC().\n"); - x2apic_ap_setup(); setup_local_APIC(false); /* Save our processor parameters. */ @@ -215,17 +210,15 @@ static void smp_callin(void) } /* Allow the master to continue. */ - set_cpu_state(CPU_STATE_CALLIN); + set_cpu_state(cpu, CPU_STATE_CALLIN); synchronize_tsc_slave(cpu); /* And wait for our final Ack. */ - while ( cpu_state != CPU_STATE_ONLINE ) + while ( cpu_state[cpu] != CPU_STATE_ONLINE ) cpu_relax(); } -static int booting_cpu; - /* CPUs for which sibling maps can be computed. */ static cpumask_t cpu_sibling_setup_map; @@ -313,18 +306,24 @@ static void set_cpu_sibling_map(unsigned int cpu) } } -void asmlinkage start_secondary(void *unused) +void asmlinkage start_secondary(unsigned int cpu) { struct cpu_info *info = get_cpu_info(); + /* Tell BSP that we are awake. */ + set_cpu_state(cpu, CPU_STATE_INIT); + /* - * Dont put anything before smp_callin(), SMP booting is so fragile that we + * Don't put anything before smp_callin(), SMP booting is so fragile that we * want to limit the things done here to the most necessary things. */ - unsigned int cpu = booting_cpu; /* Critical region without IDT or TSS. Any fault is deadly! */ + /* Wait until data set up by CPU_UP_PREPARE notifiers is ready. */ + while ( cpu_state[cpu] != CPU_STATE_CALLOUT ) + cpu_relax(); + set_current(idle_vcpu[cpu]); this_cpu(curr_vcpu) = idle_vcpu[cpu]; rdmsrl(MSR_EFER, this_cpu(efer)); @@ -348,10 +347,18 @@ void asmlinkage start_secondary(void *unused) */ spin_debug_disable(); - get_cpu_info()->use_pv_cr3 = false; - get_cpu_info()->xen_cr3 = 0; - get_cpu_info()->pv_cr3 = 0; + info->use_pv_cr3 = false; + info->xen_cr3 = 0; + info->pv_cr3 = 0; + /* + * BUG_ON() used in load_system_tables() and later code may end up calling + * machine_restart() which tries to get APIC ID for CPU running this code. + * If BSP detected that x2APIC is enabled, get_apic_id() will try to use it + * for _all_ CPUs. Enable x2APIC on secondary CPUs now so we won't end up + * with endless #GP loop. + */ + x2apic_ap_setup(); load_system_tables(); /* Full exception support from here on in. */ @@ -419,9 +426,34 @@ void asmlinkage start_secondary(void *unused) startup_cpu_idle_loop(); } +static int wake_aps_in_txt(void) +{ + struct txt_sinit_mle_data *sinit_mle = + txt_sinit_mle_data_start(__va(read_txt_reg(TXTCR_HEAP_BASE))); + uint32_t *wakeup_addr = __va(sinit_mle->rlp_wakeup_addr); + static uint32_t join[4] = {0}; + + /* Check if already started. */ + if ( join[0] != 0 ) + return -1; + + join[0] = trampoline_gdt[1]; /* GDT limit */ + join[1] = bootsym_phys(trampoline_gdt); /* GDT base */ + join[2] = TXT_AP_BOOT_CS; /* CS selector, DS = CS+8 */ + join[3] = bootsym_phys(txt_ap_entry); /* EIP */ + + write_txt_reg(TXTCR_MLE_JOIN, __pa(join)); + + smp_mb(); + + *wakeup_addr = 1; + + return 0; +} + static int wakeup_secondary_cpu(int phys_apicid, unsigned long start_eip) { - unsigned long send_status = 0, accept_status = 0; + unsigned long send_status = 0, accept_status = 0, sh = 0; int maxlvt, timeout, i; /* @@ -441,6 +473,15 @@ static int wakeup_secondary_cpu(int phys_apicid, unsigned long start_eip) if ( tboot_in_measured_env() && !tboot_wake_ap(phys_apicid, start_eip) ) return 0; + if ( ap_boot_method == AP_BOOT_TXT ) + return wake_aps_in_txt(); + + /* + * Use destination shorthand for broadcasting IPIs during boot. + */ + if ( phys_apicid == BAD_APICID ) + sh = APIC_DEST_ALLBUT; + /* * Be paranoid about clearing APIC errors. */ @@ -454,7 +495,7 @@ static int wakeup_secondary_cpu(int phys_apicid, unsigned long start_eip) /* * Turn INIT on target chip via IPI */ - apic_icr_write(APIC_INT_LEVELTRIG | APIC_INT_ASSERT | APIC_DM_INIT, + apic_icr_write(APIC_INT_LEVELTRIG | APIC_INT_ASSERT | APIC_DM_INIT | sh, phys_apicid); if ( !x2apic_enabled ) @@ -471,7 +512,7 @@ static int wakeup_secondary_cpu(int phys_apicid, unsigned long start_eip) Dprintk("Deasserting INIT.\n"); - apic_icr_write(APIC_INT_LEVELTRIG | APIC_DM_INIT, phys_apicid); + apic_icr_write(APIC_INT_LEVELTRIG | APIC_DM_INIT | sh, phys_apicid); Dprintk("Waiting for send to finish...\n"); timeout = 0; @@ -508,7 +549,7 @@ static int wakeup_secondary_cpu(int phys_apicid, unsigned long start_eip) * STARTUP IPI * Boot on the stack */ - apic_icr_write(APIC_DM_STARTUP | (start_eip >> 12), phys_apicid); + apic_icr_write(APIC_DM_STARTUP | (start_eip >> 12) | sh, phys_apicid); if ( !x2apic_enabled ) { @@ -561,7 +602,6 @@ int alloc_cpu_id(void) static int do_boot_cpu(int apicid, int cpu) { int timeout, boot_error = 0, rc = 0; - unsigned long start_eip; /* * Save current MTRR state in case it was changed since early boot @@ -569,43 +609,58 @@ static int do_boot_cpu(int apicid, int cpu) */ mtrr_save_state(); - booting_cpu = cpu; - - start_eip = bootsym_phys(trampoline_realmode_entry); - - /* start_eip needs be page aligned, and below the 1M boundary. */ - if ( start_eip & ~0xff000 ) - panic("AP trampoline %#lx not suitably positioned\n", start_eip); + /* Check if AP is already up. */ + if ( cpu_state[cpu] != CPU_STATE_INIT ) + { + /* This grunge runs the startup process for the targeted processor. */ + unsigned long start_eip; + start_eip = bootsym_phys(trampoline_realmode_entry); - /* So we see what's up */ - if ( opt_cpu_info ) - printk("Booting processor %d/%d eip %lx\n", - cpu, apicid, start_eip); + /* start_eip needs be page aligned, and below the 1M boundary. */ + if ( start_eip & ~0xff000 ) + panic("AP trampoline %#lx not suitably positioned\n", start_eip); - stack_start = stack_base[cpu] + STACK_SIZE - sizeof(struct cpu_info); + /* So we see what's up */ + if ( opt_cpu_info ) + printk("AP trampoline at %lx\n", start_eip); - /* This grunge runs the startup process for the targeted processor. */ + /* mark "stuck" area as not stuck */ + bootsym(trampoline_cpu_started) = 0; + smp_mb(); - set_cpu_state(CPU_STATE_INIT); + /* Starting actual IPI sequence... */ + boot_error = wakeup_secondary_cpu(apicid, start_eip); + } - /* Starting actual IPI sequence... */ - boot_error = wakeup_secondary_cpu(apicid, start_eip); + if ( opt_cpu_info ) + printk("Booting processor %d/%d\n", cpu, apicid); if ( !boot_error ) { - /* Allow AP to start initializing. */ - set_cpu_state(CPU_STATE_CALLOUT); - Dprintk("After Callout %d.\n", cpu); - - /* Wait 5s total for a response. */ - for ( timeout = 0; timeout < 50000; timeout++ ) + /* Wait 2s total for a response. */ + for ( timeout = 0; timeout < 20000; timeout++ ) { - if ( cpu_state != CPU_STATE_CALLOUT ) + if ( cpu_state[cpu] == CPU_STATE_INIT ) break; udelay(100); } - if ( cpu_state == CPU_STATE_CALLIN ) + if ( cpu_state[cpu] == CPU_STATE_INIT ) + { + /* Allow AP to start initializing. */ + set_cpu_state(cpu, CPU_STATE_CALLOUT); + Dprintk("After Callout %d.\n", cpu); + + /* Wait 5s total for a response. */ + for ( timeout = 0; timeout < 500000; timeout++ ) + { + if ( cpu_state[cpu] != CPU_STATE_CALLOUT ) + break; + udelay(10); + } + } + + if ( cpu_state[cpu] == CPU_STATE_CALLIN ) { /* number CPUs logically, starting from 1 (BSP is 0) */ Dprintk("OK.\n"); @@ -613,7 +668,7 @@ static int do_boot_cpu(int apicid, int cpu) synchronize_tsc_master(cpu); Dprintk("CPU has booted.\n"); } - else if ( cpu_state == CPU_STATE_DEAD ) + else if ( cpu_state[cpu] == CPU_STATE_DEAD ) { smp_rmb(); rc = cpu_error; @@ -637,10 +692,6 @@ static int do_boot_cpu(int apicid, int cpu) rc = -EIO; } - /* mark "stuck" area as not stuck */ - bootsym(trampoline_cpu_started) = 0; - smp_mb(); - return rc; } @@ -684,7 +735,7 @@ unsigned long alloc_stub_page(unsigned int cpu, unsigned long *mfn) void cpu_exit_clear(unsigned int cpu) { cpu_uninit(cpu); - set_cpu_state(CPU_STATE_DEAD); + set_cpu_state(cpu, CPU_STATE_DEAD); } static int clone_mapping(const void *ptr, root_pgentry_t *rpt) @@ -1146,8 +1197,32 @@ static struct notifier_block cpu_smpboot_nfb = { .notifier_call = cpu_smpboot_callback }; +void smp_send_init_sipi_sipi_allbutself(void) +{ + unsigned long start_eip; + start_eip = bootsym_phys(trampoline_realmode_entry); + + /* start_eip needs be page aligned, and below the 1M boundary. */ + if ( start_eip & ~0xff000 ) + panic("AP trampoline %#lx not suitably positioned\n", start_eip); + + /* So we see what's up */ + if ( opt_cpu_info ) + printk("Booting APs in parallel, eip %lx\n", start_eip); + + /* Starting actual broadcast IPI sequence... */ + wakeup_secondary_cpu(BAD_APICID, start_eip); +} + void __init smp_prepare_cpus(void) { + /* + * If the platform is performing a Secure Launch via TXT, secondary + * CPUs (APs) will need to be woken up in a TXT-specific way. + */ + if ( slaunch_active && boot_cpu_data.x86_vendor == X86_VENDOR_INTEL ) + ap_boot_method = AP_BOOT_TXT; + register_cpu_notifier(&cpu_smpboot_nfb); mtrr_aps_sync_begin(); @@ -1157,10 +1232,13 @@ void __init smp_prepare_cpus(void) print_cpu_info(0); boot_cpu_physical_apicid = get_apic_id(); - x86_cpu_to_apicid[0] = boot_cpu_physical_apicid; + cpu_physical_id(0) = boot_cpu_physical_apicid; stack_base[0] = (void *)((unsigned long)stack_start & ~(STACK_SIZE - 1)); + /* Not really used anywhere, but set it just in case. */ + set_cpu_state(0, CPU_STATE_ONLINE); + set_nr_sockets(); socket_cpumask = xzalloc_array(cpumask_t *, nr_sockets); @@ -1267,7 +1345,7 @@ void __cpu_disable(void) { int cpu = smp_processor_id(); - set_cpu_state(CPU_STATE_DYING); + set_cpu_state(cpu, CPU_STATE_DYING); local_irq_disable(); clear_local_APIC(); @@ -1292,7 +1370,7 @@ void __cpu_die(unsigned int cpu) unsigned int i = 0; enum cpu_state seen_state; - while ( (seen_state = cpu_state) != CPU_STATE_DEAD ) + while ( (seen_state = cpu_state[cpu]) != CPU_STATE_DEAD ) { BUG_ON(seen_state != CPU_STATE_DYING); mdelay(100); @@ -1377,7 +1455,7 @@ int __cpu_up(unsigned int cpu) { int apicid, ret; - if ( (apicid = x86_cpu_to_apicid[cpu]) == BAD_APICID ) + if ( (apicid = cpu_physical_id(cpu)) == BAD_APICID ) return -ENODEV; if ( (!x2apic_enabled && apicid >= APIC_ALL_CPUS) || @@ -1393,7 +1471,7 @@ int __cpu_up(unsigned int cpu) time_latch_stamps(); - set_cpu_state(CPU_STATE_ONLINE); + set_cpu_state(cpu, CPU_STATE_ONLINE); while ( !cpu_online(cpu) ) { cpu_relax(); diff --git a/xen/arch/x86/spec_ctrl.c b/xen/arch/x86/spec_ctrl.c index 75a4177a75..21b974936f 100644 --- a/xen/arch/x86/spec_ctrl.c +++ b/xen/arch/x86/spec_ctrl.c @@ -707,7 +707,7 @@ static bool __init check_smt_enabled(void) * has a non-zero thread id component indicates that SMT is active. */ for_each_present_cpu ( cpu ) - if ( x86_cpu_to_apicid[cpu] & (boot_cpu_data.x86_num_siblings - 1) ) + if ( cpu_physical_id(cpu) & (boot_cpu_data.x86_num_siblings - 1) ) return true; return false; diff --git a/xen/arch/x86/sysctl.c b/xen/arch/x86/sysctl.c index 1b04947516..3aeca75909 100644 --- a/xen/arch/x86/sysctl.c +++ b/xen/arch/x86/sysctl.c @@ -58,7 +58,7 @@ static long cf_check smt_up_down_helper(void *data) for_each_present_cpu ( cpu ) { /* Skip primary siblings (those whose thread id is 0). */ - if ( !(x86_cpu_to_apicid[cpu] & sibling_mask) ) + if ( !(cpu_physical_id(cpu) & sibling_mask) ) continue; if ( !up && core_parking_remove(cpu) ) diff --git a/xen/arch/x86/tboot.c b/xen/arch/x86/tboot.c index d5db60d335..f68354c374 100644 --- a/xen/arch/x86/tboot.c +++ b/xen/arch/x86/tboot.c @@ -15,6 +15,7 @@ #include #include #include +#include #include @@ -35,23 +36,6 @@ static uint64_t __initdata sinit_base, __initdata sinit_size; static bool __ro_after_init is_vtd; -/* - * TXT configuration registers (offsets from TXT_{PUB, PRIV}_CONFIG_REGS_BASE) - */ - -#define TXT_PUB_CONFIG_REGS_BASE 0xfed30000 -#define TXT_PRIV_CONFIG_REGS_BASE 0xfed20000 - -/* # pages for each config regs space - used by fixmap */ -#define NR_TXT_CONFIG_PAGES ((TXT_PUB_CONFIG_REGS_BASE - \ - TXT_PRIV_CONFIG_REGS_BASE) >> PAGE_SHIFT) - -/* offsets from pub/priv config space */ -#define TXTCR_SINIT_BASE 0x0270 -#define TXTCR_SINIT_SIZE 0x0278 -#define TXTCR_HEAP_BASE 0x0300 -#define TXTCR_HEAP_SIZE 0x0308 - #define SHA1_SIZE 20 typedef uint8_t sha1_hash_t[SHA1_SIZE]; @@ -409,7 +393,7 @@ int __init tboot_protect_mem_regions(void) /* TXT Private Space */ rc = e820_change_range_type(&e820, TXT_PRIV_CONFIG_REGS_BASE, - TXT_PRIV_CONFIG_REGS_BASE + NR_TXT_CONFIG_PAGES * PAGE_SIZE, + TXT_PRIV_CONFIG_REGS_BASE + NR_TXT_CONFIG_SIZE, E820_RESERVED, E820_UNUSABLE); if ( !rc ) return 0; diff --git a/xen/arch/x86/tpm.c b/xen/arch/x86/tpm.c new file mode 100644 index 0000000000..c7d5bd23ce --- /dev/null +++ b/xen/arch/x86/tpm.c @@ -0,0 +1,1038 @@ +/* + * Copyright (c) 2022-2024 3mdeb Sp. z o.o. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include + +#ifdef __EARLY_TPM__ + +#ifdef __va +#error "__va defined in non-paged mode!" +#endif + +#define __va(x) _p(x) + +uint32_t slaunch_slrt; + +/* + * The code is being compiled as a standalone binary without linking to any + * other part of Xen. Providing implementation of builtin functions in this + * case is necessary if compiler chooses to not use an inline builtin. + */ +void *memset(void *dest, int c, size_t n) +{ + uint8_t *d = dest; + + while ( n-- ) + *d++ = c; + + return dest; +} +void *memcpy(void *dest, const void *src, size_t n) +{ + const uint8_t *s = src; + uint8_t *d = dest; + + while ( n-- ) + *d++ = *s++; + + return dest; +} + +static bool is_amd_cpu(void) +{ + /* + * asm/processor.h can't be included in early code, which means neither + * cpuid() function nor boot_cpu_data can be used here. + */ + uint32_t eax, ebx, ecx, edx; + asm volatile ( "cpuid" + : "=a" (eax), "=b" (ebx), "=c" (ecx), "=d" (edx) + : "0" (0), "c" (0) ); + return ebx == X86_VENDOR_AMD_EBX + && ecx == X86_VENDOR_AMD_ECX + && edx == X86_VENDOR_AMD_EDX; +} + +#else /* __EARLY_TPM__ */ + +#include +#include + +static bool is_amd_cpu(void) +{ + return boot_cpu_data.x86_vendor == X86_VENDOR_AMD; +} + +#endif /* __EARLY_TPM__ */ + +#define TPM_LOC_REG(loc, reg) (0x1000 * (loc) + (reg)) + +#define TPM_ACCESS_(x) TPM_LOC_REG(x, 0x00) +#define ACCESS_REQUEST_USE (1 << 1) +#define ACCESS_ACTIVE_LOCALITY (1 << 5) +#define TPM_INTF_CAPABILITY_(x) TPM_LOC_REG(x, 0x14) +#define INTF_VERSION_MASK 0x70000000 +#define TPM_STS_(x) TPM_LOC_REG(x, 0x18) +#define TPM_FAMILY_MASK 0x0C000000 +#define STS_DATA_AVAIL (1 << 4) +#define STS_TPM_GO (1 << 5) +#define STS_COMMAND_READY (1 << 6) +#define STS_VALID (1 << 7) +#define TPM_DATA_FIFO_(x) TPM_LOC_REG(x, 0x24) + +#define swap16(x) __builtin_bswap16(x) +#define swap32(x) __builtin_bswap32(x) +#define memset(s, c, n) __builtin_memset(s, c, n) +#define memcpy(d, s, n) __builtin_memcpy(d, s, n) + +static inline volatile uint32_t tis_read32(unsigned reg) +{ + return *(volatile uint32_t *)__va(TPM_TIS_BASE + reg); +} + +static inline volatile uint8_t tis_read8(unsigned reg) +{ + return *(volatile uint8_t *)__va(TPM_TIS_BASE + reg); +} + +static inline void tis_write8(unsigned reg, uint8_t val) +{ + *(volatile uint8_t *)__va(TPM_TIS_BASE + reg) = val; +} + +static inline void request_locality(unsigned loc) +{ + tis_write8(TPM_ACCESS_(loc), ACCESS_REQUEST_USE); + /* Check that locality was actually activated. */ + while ( (tis_read8(TPM_ACCESS_(loc)) & ACCESS_ACTIVE_LOCALITY) == 0 ); +} + +static inline void relinquish_locality(unsigned loc) +{ + tis_write8(TPM_ACCESS_(loc), ACCESS_ACTIVE_LOCALITY); +} + +static void send_cmd(unsigned loc, uint8_t *buf, unsigned i_size, + unsigned *o_size) +{ + /* + * Value of "data available" bit counts only when "valid" field is set as + * well. + */ + const unsigned data_avail = STS_VALID | STS_DATA_AVAIL; + + unsigned i; + + /* Make sure TPM can accept a command. */ + if ( (tis_read8(TPM_STS_(loc)) & STS_COMMAND_READY) == 0 ) { + /* Abort current command. */ + tis_write8(TPM_STS_(loc), STS_COMMAND_READY); + /* Wait until TPM is ready for a new one. */ + while ( (tis_read8(TPM_STS_(loc)) & STS_COMMAND_READY) == 0 ); + } + + for ( i = 0; i < i_size; i++ ) + tis_write8(TPM_DATA_FIFO_(loc), buf[i]); + + tis_write8(TPM_STS_(loc), STS_TPM_GO); + + /* Wait for the first byte of response. */ + while ( (tis_read8(TPM_STS_(loc)) & data_avail) != data_avail); + + for ( i = 0; i < *o_size && tis_read8(TPM_STS_(loc)) & data_avail; i++ ) + buf[i] = tis_read8(TPM_DATA_FIFO_(loc)); + + if ( i < *o_size ) + *o_size = i; + + tis_write8(TPM_STS_(loc), STS_COMMAND_READY); +} + +static inline bool is_tpm12(void) +{ + /* + * If one of these conditions is true: + * - INTF_CAPABILITY_x.interfaceVersion is 0 (TIS <= 1.21) + * - INTF_CAPABILITY_x.interfaceVersion is 2 (TIS == 1.3) + * - STS_x.tpmFamily is 0 + * we're dealing with TPM1.2. + */ + uint32_t intf_version = tis_read32(TPM_INTF_CAPABILITY_(0)) + & INTF_VERSION_MASK; + return (intf_version == 0x00000000 || intf_version == 0x20000000 || + (tis_read32(TPM_STS_(0)) & TPM_FAMILY_MASK) == 0); +} + +/****************************** TPM1.2 & TPM2.0 *******************************/ + +/* + * TPM1.2 is required to support commands of up to 1101 bytes, vendors rarely + * go above that. Limit maximum size of block of data to be hashed to 1024. + * + * TPM2.0 should support hashing of at least 1024 bytes. + */ +#define MAX_HASH_BLOCK 1024 + +/* All fields of following structs are big endian. */ +struct tpm_cmd_hdr { + uint16_t tag; + uint32_t paramSize; + uint32_t ordinal; +} __packed; + +struct tpm_rsp_hdr { + uint16_t tag; + uint32_t paramSize; + uint32_t returnCode; +} __packed; + +/****************************** TPM1.2 specific *******************************/ + +#define TPM_ORD_Extend 0x00000014 +#define TPM_ORD_SHA1Start 0x000000A0 +#define TPM_ORD_SHA1Update 0x000000A1 +#define TPM_ORD_SHA1CompleteExtend 0x000000A3 + +#define TPM_TAG_RQU_COMMAND 0x00C1 +#define TPM_TAG_RSP_COMMAND 0x00C4 + +/* All fields of following structs are big endian. */ +struct extend_cmd { + struct tpm_cmd_hdr h; + uint32_t pcrNum; + uint8_t inDigest[SHA1_DIGEST_SIZE]; +} __packed; + +struct extend_rsp { + struct tpm_rsp_hdr h; + uint8_t outDigest[SHA1_DIGEST_SIZE]; +} __packed; + +struct sha1_start_cmd { + struct tpm_cmd_hdr h; +} __packed; + +struct sha1_start_rsp { + struct tpm_rsp_hdr h; + uint32_t maxNumBytes; +} __packed; + +struct sha1_update_cmd { + struct tpm_cmd_hdr h; + uint32_t numBytes; /* Must be a multiple of 64 */ + uint8_t hashData[]; +} __packed; + +struct sha1_update_rsp { + struct tpm_rsp_hdr h; +} __packed; + +struct sha1_complete_extend_cmd { + struct tpm_cmd_hdr h; + uint32_t pcrNum; + uint32_t hashDataSize; /* 0-64, inclusive */ + uint8_t hashData[]; +} __packed; + +struct sha1_complete_extend_rsp { + struct tpm_rsp_hdr h; + uint8_t hashValue[SHA1_DIGEST_SIZE]; + uint8_t outDigest[SHA1_DIGEST_SIZE]; +} __packed; + +struct TPM12_PCREvent { + uint32_t PCRIndex; + uint32_t Type; + uint8_t Digest[SHA1_DIGEST_SIZE]; + uint32_t Size; + uint8_t Data[]; +}; + +struct tpm1_spec_id_event { + uint32_t pcrIndex; + uint32_t eventType; + uint8_t digest[20]; + uint32_t eventSize; + uint8_t signature[16]; + uint32_t platformClass; + uint8_t specVersionMinor; + uint8_t specVersionMajor; + uint8_t specErrata; + uint8_t uintnSize; + uint8_t vendorInfoSize; + uint8_t vendorInfo[0]; /* variable number of members */ +} __packed; + +struct txt_ev_log_container_12 { + char Signature[20]; /* "TXT Event Container", null-terminated */ + uint8_t Reserved[12]; + uint8_t ContainerVerMajor; + uint8_t ContainerVerMinor; + uint8_t PCREventVerMajor; + uint8_t PCREventVerMinor; + uint32_t ContainerSize; /* Allocated size */ + uint32_t PCREventsOffset; + uint32_t NextEventOffset; + struct TPM12_PCREvent PCREvents[]; +}; + +#ifdef __EARLY_TPM__ +#define CMD_RSP_BUF_SIZE (sizeof(struct sha1_update_cmd) + MAX_HASH_BLOCK) + +union cmd_rsp { + struct sha1_start_cmd start_c; + struct sha1_start_rsp start_r; + struct sha1_update_cmd update_c; + struct sha1_update_rsp update_r; + struct sha1_complete_extend_cmd finish_c; + struct sha1_complete_extend_rsp finish_r; + uint8_t buf[CMD_RSP_BUF_SIZE]; +}; + +/* Returns true on success. */ +static bool tpm12_hash_extend(unsigned loc, uint8_t *buf, unsigned size, + unsigned pcr, uint8_t *out_digest) +{ + union cmd_rsp cmd_rsp; + unsigned max_bytes = MAX_HASH_BLOCK; + unsigned o_size = sizeof(cmd_rsp); + bool success = false; + + request_locality(loc); + + cmd_rsp.start_c = (struct sha1_start_cmd) { + .h.tag = swap16(TPM_TAG_RQU_COMMAND), + .h.paramSize = swap32(sizeof(struct sha1_start_cmd)), + .h.ordinal = swap32(TPM_ORD_SHA1Start), + }; + + send_cmd(loc, cmd_rsp.buf, sizeof(struct sha1_start_cmd), &o_size); + if ( o_size < sizeof(struct sha1_start_rsp) ) + goto error; + + if ( max_bytes > swap32(cmd_rsp.start_r.maxNumBytes) ) + max_bytes = swap32(cmd_rsp.start_r.maxNumBytes); + + while ( size > 64 ) { + if ( size < max_bytes ) + max_bytes = size & ~(64 - 1); + + o_size = sizeof(cmd_rsp); + + cmd_rsp.update_c = (struct sha1_update_cmd){ + .h.tag = swap16(TPM_TAG_RQU_COMMAND), + .h.paramSize = swap32(sizeof(struct sha1_update_cmd) + max_bytes), + .h.ordinal = swap32(TPM_ORD_SHA1Update), + .numBytes = swap32(max_bytes), + }; + memcpy(cmd_rsp.update_c.hashData, buf, max_bytes); + + send_cmd(loc, cmd_rsp.buf, sizeof(struct sha1_update_cmd) + max_bytes, + &o_size); + if ( o_size < sizeof(struct sha1_update_rsp) ) + goto error; + + size -= max_bytes; + buf += max_bytes; + } + + o_size = sizeof(cmd_rsp); + + cmd_rsp.finish_c = (struct sha1_complete_extend_cmd) { + .h.tag = swap16(TPM_TAG_RQU_COMMAND), + .h.paramSize = swap32(sizeof(struct sha1_complete_extend_cmd) + size), + .h.ordinal = swap32(TPM_ORD_SHA1CompleteExtend), + .pcrNum = swap32(pcr), + .hashDataSize = swap32(size), + }; + memcpy(cmd_rsp.finish_c.hashData, buf, size); + + send_cmd(loc, cmd_rsp.buf, sizeof(struct sha1_complete_extend_cmd) + size, + &o_size); + if ( o_size < sizeof(struct sha1_complete_extend_rsp) ) + goto error; + + if ( out_digest != NULL ) + memcpy(out_digest, cmd_rsp.finish_r.hashValue, SHA1_DIGEST_SIZE); + + success = true; + +error: + relinquish_locality(loc); + return success; +} + +#else + +union cmd_rsp { + struct extend_cmd extend_c; + struct extend_rsp extend_r; +}; + +/* Returns true on success. */ +static bool tpm12_hash_extend(unsigned loc, uint8_t *buf, unsigned size, + unsigned pcr, uint8_t *out_digest) +{ + union cmd_rsp cmd_rsp; + unsigned o_size = sizeof(cmd_rsp); + + sha1_hash(buf, size, out_digest); + + request_locality(loc); + + cmd_rsp.extend_c = (struct extend_cmd) { + .h.tag = swap16(TPM_TAG_RQU_COMMAND), + .h.paramSize = swap32(sizeof(struct extend_cmd)), + .h.ordinal = swap32(TPM_ORD_Extend), + .pcrNum = swap32(pcr), + }; + + memcpy(cmd_rsp.extend_c.inDigest, out_digest, SHA1_DIGEST_SIZE); + + send_cmd(loc, (uint8_t *)&cmd_rsp, sizeof(struct extend_cmd), &o_size); + + relinquish_locality(loc); + + return (o_size >= sizeof(struct extend_rsp)); +} + +#endif /* __EARLY_TPM__ */ + +static void *create_log_event12(struct txt_ev_log_container_12 *evt_log, + uint32_t evt_log_size, uint32_t pcr, + uint32_t type, uint8_t *data, + unsigned data_size) +{ + struct TPM12_PCREvent *new_entry; + + if ( is_amd_cpu() ) { + /* + * On AMD, TXT-compatible structure is stored as vendor data of + * TCG-defined event log header. + */ + struct tpm1_spec_id_event *spec_id = (void *)evt_log; + evt_log = (struct txt_ev_log_container_12 *)&spec_id->vendorInfo[0]; + } + + new_entry = (void *)(((uint8_t *)evt_log) + evt_log->NextEventOffset); + + /* + * Check if there is enough space left for new entry. + * Note: it is possible to introduce a gap in event log if entry with big + * data_size is followed by another entry with smaller data. Maybe we should + * cap the event log size in such case? + */ + if ( evt_log->NextEventOffset + sizeof(struct TPM12_PCREvent) + data_size + > evt_log_size ) + return NULL; + + evt_log->NextEventOffset += sizeof(struct TPM12_PCREvent) + data_size; + + new_entry->PCRIndex = pcr; + new_entry->Type = type; + new_entry->Size = data_size; + + if ( data && data_size > 0 ) + memcpy(new_entry->Data, data, data_size); + + return new_entry->Digest; +} + +/************************** end of TPM1.2 specific ****************************/ + +/****************************** TPM2.0 specific *******************************/ + +/* + * These constants are for TPM2.0 but don't have a distinct prefix to match + * names in the specification. + */ + +#define TPM_HT_PCR 0x00 + +#define TPM_RH_NULL 0x40000007 +#define TPM_RS_PW 0x40000009 + +#define HR_SHIFT 24 +#define HR_PCR (TPM_HT_PCR << HR_SHIFT) + +#define TPM_ST_NO_SESSIONS 0x8001 +#define TPM_ST_SESSIONS 0x8002 + +#define TPM_ALG_SHA1 0x0004 +#define TPM_ALG_SHA256 0x000b +#define TPM_ALG_NULL 0x0010 + +#define TPM2_PCR_Extend 0x00000182 +#define TPM2_PCR_HashSequenceStart 0x00000186 +#define TPM2_PCR_SequenceUpdate 0x0000015C +#define TPM2_PCR_EventSequenceComplete 0x00000185 + +#define PUT_BYTES(p, bytes, size) do { \ + memcpy((p), (bytes), (size)); \ + (p) += (size); \ + } while ( 0 ) + +#define PUT_16BIT(p, data) do { \ + *(uint16_t *)(p) = swap16(data); \ + (p) += 2; \ + } while ( 0 ) + +/* All fields of following structs are big endian. */ +struct tpm2_session_header { + uint32_t handle; + uint16_t nonceSize; + uint8_t nonce[0]; + uint8_t attrs; + uint16_t hmacSize; + uint8_t hmac[0]; +} __packed; + +struct tpm2_extend_cmd { + struct tpm_cmd_hdr h; + uint32_t pcrHandle; + uint32_t sessionHdrSize; + struct tpm2_session_header pcrSession; + uint32_t hashCount; + uint8_t hashes[0]; +} __packed; + +struct tpm2_extend_rsp { + struct tpm_rsp_hdr h; +} __packed; + +struct tpm2_sequence_start_cmd { + struct tpm_cmd_hdr h; + uint16_t hmacSize; + uint8_t hmac[0]; + uint16_t hashAlg; +} __packed; + +struct tpm2_sequence_start_rsp { + struct tpm_rsp_hdr h; + uint32_t sequenceHandle; +} __packed; + +struct tpm2_sequence_update_cmd { + struct tpm_cmd_hdr h; + uint32_t sequenceHandle; + uint32_t sessionHdrSize; + struct tpm2_session_header session; + uint16_t dataSize; + uint8_t data[0]; +} __packed; + +struct tpm2_sequence_update_rsp { + struct tpm_rsp_hdr h; +} __packed; + +struct tpm2_sequence_complete_cmd { + struct tpm_cmd_hdr h; + uint32_t pcrHandle; + uint32_t sequenceHandle; + uint32_t sessionHdrSize; + struct tpm2_session_header pcrSession; + struct tpm2_session_header sequenceSession; + uint16_t dataSize; + uint8_t data[0]; +} __packed; + +struct tpm2_sequence_complete_rsp { + struct tpm_rsp_hdr h; + uint32_t paramSize; + uint32_t hashCount; + uint8_t hashes[0]; + /* + * Each hash is represented as: + * struct { + * uint16_t hashAlg; + * uint8_t hash[size of hashAlg]; + * }; + */ +} __packed; + +/* + * These two structure are for convenience, they don't correspond to anything in + * any spec. + */ +struct tpm2_log_hash { + uint16_t alg; /* TPM_ALG_* */ + uint16_t size; + uint8_t *data; /* Non-owning reference to a buffer inside log entry. */ +}; +/* Should be more than enough for now and awhile in the future. */ +#define MAX_HASH_COUNT 8 +struct tpm2_log_hashes { + uint32_t count; + struct tpm2_log_hash hashes[MAX_HASH_COUNT]; +}; + +struct tpm2_pcr_event_header { + uint32_t pcrIndex; + uint32_t eventType; + uint32_t digestCount; + uint8_t digests[0]; + /* + * Each hash is represented as: + * struct { + * uint16_t hashAlg; + * uint8_t hash[size of hashAlg]; + * }; + */ + /* uint32_t eventSize; */ + /* uint8_t event[0]; */ +} __packed; + +struct tpm2_digest_sizes { + uint16_t algId; + uint16_t digestSize; +} __packed; + +struct tpm2_spec_id_event { + uint32_t pcrIndex; + uint32_t eventType; + uint8_t digest[20]; + uint32_t eventSize; + uint8_t signature[16]; + uint32_t platformClass; + uint8_t specVersionMinor; + uint8_t specVersionMajor; + uint8_t specErrata; + uint8_t uintnSize; + uint32_t digestCount; + struct tpm2_digest_sizes digestSizes[0]; /* variable number of members */ + /* uint8_t vendorInfoSize; */ + /* uint8_t vendorInfo[vendorInfoSize]; */ +} __packed; + +#ifdef __EARLY_TPM__ + +union tpm2_cmd_rsp { + uint8_t b[sizeof(struct tpm2_sequence_update_cmd) + MAX_HASH_BLOCK]; + struct tpm_cmd_hdr c; + struct tpm_rsp_hdr r; + struct tpm2_sequence_start_cmd start_c; + struct tpm2_sequence_start_rsp start_r; + struct tpm2_sequence_update_cmd update_c; + struct tpm2_sequence_update_rsp update_r; + struct tpm2_sequence_complete_cmd finish_c; + struct tpm2_sequence_complete_rsp finish_r; +}; + +static uint32_t tpm2_hash_extend(unsigned loc, uint8_t *buf, unsigned size, + unsigned pcr, + struct tpm2_log_hashes *log_hashes) +{ + uint32_t seq_handle; + unsigned max_bytes = MAX_HASH_BLOCK; + + union tpm2_cmd_rsp cmd_rsp; + unsigned o_size; + unsigned i; + uint8_t *p; + uint32_t rc; + + cmd_rsp.start_c = (struct tpm2_sequence_start_cmd) { + .h.tag = swap16(TPM_ST_NO_SESSIONS), + .h.paramSize = swap32(sizeof(cmd_rsp.start_c)), + .h.ordinal = swap32(TPM2_PCR_HashSequenceStart), + .hashAlg = swap16(TPM_ALG_NULL), /* Compute all supported hashes. */ + }; + + request_locality(loc); + + o_size = sizeof(cmd_rsp); + send_cmd(loc, cmd_rsp.b, swap32(cmd_rsp.c.paramSize), &o_size); + + if ( cmd_rsp.r.tag == swap16(TPM_ST_NO_SESSIONS) && + cmd_rsp.r.paramSize == swap32(10) ) { + rc = swap32(cmd_rsp.r.returnCode); + if ( rc != 0 ) + goto error; + } + + seq_handle = swap32(cmd_rsp.start_r.sequenceHandle); + + while ( size > 64 ) { + if ( size < max_bytes ) + max_bytes = size & ~(64 - 1); + + cmd_rsp.update_c = (struct tpm2_sequence_update_cmd) { + .h.tag = swap16(TPM_ST_SESSIONS), + .h.paramSize = swap32(sizeof(cmd_rsp.update_c) + max_bytes), + .h.ordinal = swap32(TPM2_PCR_SequenceUpdate), + .sequenceHandle = swap32(seq_handle), + .sessionHdrSize = swap32(sizeof(struct tpm2_session_header)), + .session.handle = swap32(TPM_RS_PW), + .dataSize = swap16(max_bytes), + }; + + memcpy(cmd_rsp.update_c.data, buf, max_bytes); + + o_size = sizeof(cmd_rsp); + send_cmd(loc, cmd_rsp.b, swap32(cmd_rsp.c.paramSize), &o_size); + + if ( cmd_rsp.r.tag == swap16(TPM_ST_NO_SESSIONS) && + cmd_rsp.r.paramSize == swap32(10) ) { + rc = swap32(cmd_rsp.r.returnCode); + if ( rc != 0 ) + goto error; + } + + size -= max_bytes; + buf += max_bytes; + } + + cmd_rsp.finish_c = (struct tpm2_sequence_complete_cmd) { + .h.tag = swap16(TPM_ST_SESSIONS), + .h.paramSize = swap32(sizeof(cmd_rsp.finish_c) + size), + .h.ordinal = swap32(TPM2_PCR_EventSequenceComplete), + .pcrHandle = swap32(HR_PCR + pcr), + .sequenceHandle = swap32(seq_handle), + .sessionHdrSize = swap32(sizeof(struct tpm2_session_header)*2), + .pcrSession.handle = swap32(TPM_RS_PW), + .sequenceSession.handle = swap32(TPM_RS_PW), + .dataSize = swap16(size), + }; + + memcpy(cmd_rsp.finish_c.data, buf, size); + + o_size = sizeof(cmd_rsp); + send_cmd(loc, cmd_rsp.b, swap32(cmd_rsp.c.paramSize), &o_size); + + if ( cmd_rsp.r.tag == swap16(TPM_ST_NO_SESSIONS) && + cmd_rsp.r.paramSize == swap32(10) ) { + rc = swap32(cmd_rsp.r.returnCode); + if ( rc != 0 ) + goto error; + } + + p = cmd_rsp.finish_r.hashes; + for ( i = 0; i < swap32(cmd_rsp.finish_r.hashCount); ++i ) { + unsigned j; + uint16_t hash_type; + + hash_type = swap16(*(uint16_t *)p); + p += sizeof(uint16_t); + + for ( j = 0; j < log_hashes->count; ++j ) { + struct tpm2_log_hash *hash = &log_hashes->hashes[j]; + if ( hash->alg == hash_type ) { + memcpy(hash->data, p, hash->size); + p += hash->size; + break; + } + } + + if ( j == log_hashes->count ) { + /* Can't continue parsing without knowing hash size. */ + break; + } + } + + rc = 0; + +error: + relinquish_locality(loc); + return rc; +} + +#else + +union tpm2_cmd_rsp { + /* Enough space for multiple hashes. */ + uint8_t b[sizeof(struct tpm2_extend_cmd) + 1024]; + struct tpm_cmd_hdr c; + struct tpm_rsp_hdr r; + struct tpm2_extend_cmd extend_c; + struct tpm2_extend_rsp extend_r; +}; + +static uint32_t tpm20_pcr_extend(unsigned loc, uint32_t pcr_handle, + const struct tpm2_log_hashes *log_hashes) +{ + union tpm2_cmd_rsp cmd_rsp; + unsigned o_size; + unsigned i; + uint8_t *p; + + cmd_rsp.extend_c = (struct tpm2_extend_cmd) { + .h.tag = swap16(TPM_ST_SESSIONS), + .h.ordinal = swap32(TPM2_PCR_Extend), + .pcrHandle = swap32(pcr_handle), + .sessionHdrSize = swap32(sizeof(struct tpm2_session_header)), + .pcrSession.handle = swap32(TPM_RS_PW), + .hashCount = swap32(log_hashes->count), + }; + + p = cmd_rsp.extend_c.hashes; + for ( i = 0; i < log_hashes->count; ++i ) { + const struct tpm2_log_hash *hash = &log_hashes->hashes[i]; + + if ( p + sizeof(uint16_t) + hash->size > &cmd_rsp.b[sizeof(cmd_rsp)] ) { + printk(XENLOG_ERR "Hit TPM message size implementation limit: %ld\n", + sizeof(cmd_rsp)); + return -1; + } + + *(uint16_t *)p = swap16(hash->alg); + p += sizeof(uint16_t); + + memcpy(p, hash->data, hash->size); + p += hash->size; + } + + /* Fill in command size (size of the whole buffer). */ + cmd_rsp.extend_c.h.paramSize = swap32(sizeof(cmd_rsp.extend_c) + + (p - cmd_rsp.extend_c.hashes)), + + o_size = sizeof(cmd_rsp); + send_cmd(loc, cmd_rsp.b, swap32(cmd_rsp.c.paramSize), &o_size); + + return swap32(cmd_rsp.r.returnCode); +} + +static bool tpm_supports_hash(unsigned loc, const struct tpm2_log_hash *hash) +{ + uint32_t rc; + struct tpm2_log_hashes hashes = { + .count = 1, + .hashes[0] = *hash, + }; + + /* This is a valid way of checking hash support, using it to not implement + * TPM2_GetCapability(). */ + rc = tpm20_pcr_extend(loc, /*pcr_handle=*/TPM_RH_NULL, &hashes); + + return rc == 0; +} + +static uint32_t tpm2_hash_extend(unsigned loc, uint8_t *buf, unsigned size, + unsigned pcr, + const struct tpm2_log_hashes *log_hashes) +{ + uint32_t rc; + unsigned i; + struct tpm2_log_hashes supported_hashes = {0}; + + request_locality(loc); + + for ( i = 0; i < log_hashes->count; ++i ) { + const struct tpm2_log_hash *hash = &log_hashes->hashes[i]; + if ( !tpm_supports_hash(loc, hash) ) { + printk(XENLOG_WARNING "Skipped hash unsupported by TPM: %d\n", + hash->alg); + continue; + } + + if ( hash->alg == TPM_ALG_SHA1 ) + sha1_hash(buf, size, hash->data); + else if ( hash->alg == TPM_ALG_SHA256 ) + sha256_hash(buf, size, hash->data); + else + /* create_log_event20() took care of initializing the digest. */; + + if ( supported_hashes.count == MAX_HASH_COUNT ) { + printk(XENLOG_ERR "Hit hash count implementation limit: %d\n", + MAX_HASH_COUNT); + return -1; + } + + supported_hashes.hashes[supported_hashes.count] = *hash; + ++supported_hashes.count; + } + + rc = tpm20_pcr_extend(loc, HR_PCR + pcr, &supported_hashes); + relinquish_locality(loc); + + return rc; +} + +#endif /* __EARLY_TPM__ */ + +static struct heap_event_log_pointer_element2_1 * +find_evt_log_ext_data(struct tpm2_spec_id_event *evt_log) +{ + struct txt_os_sinit_data *os_sinit; + struct txt_ext_data_element *ext_data; + + if ( is_amd_cpu() ) { + /* + * Event log pointer is defined by TXT specification, but + * secure-kernel-loader provides a compatible structure in vendor data + * of the log. + */ + const uint8_t *data_size = + (void *)&evt_log->digestSizes[evt_log->digestCount]; + + if ( *data_size != sizeof(struct heap_event_log_pointer_element2_1) ) + return NULL; + + /* Vendor data directly follows one-byte size. */ + return (void *)(data_size + 1); + } + + os_sinit = txt_os_sinit_data_start(__va(read_txt_reg(TXTCR_HEAP_BASE))); + ext_data = (void *)((uint8_t *)os_sinit + sizeof(*os_sinit)); + + /* + * Find TXT_HEAP_EXTDATA_TYPE_EVENT_LOG_POINTER2_1 which is necessary to + * know where to put the next entry. + */ + while ( ext_data->type != TXT_HEAP_EXTDATA_TYPE_END ) { + if ( ext_data->type == TXT_HEAP_EXTDATA_TYPE_EVENT_LOG_POINTER2_1 ) + break; + ext_data = (void *)&ext_data->data[ext_data->size]; + } + + if ( ext_data->type == TXT_HEAP_EXTDATA_TYPE_END ) + return NULL; + + return (void *)&ext_data->data[0]; +} + +static struct tpm2_log_hashes +create_log_event20(struct tpm2_spec_id_event *evt_log, uint32_t evt_log_size, + uint32_t pcr, uint32_t type, uint8_t *data, + unsigned data_size) +{ + struct tpm2_log_hashes log_hashes = {0}; + + struct heap_event_log_pointer_element2_1 *log_ext_data; + struct tpm2_pcr_event_header *new_entry; + uint32_t entry_size; + unsigned i; + uint8_t *p; + + log_ext_data = find_evt_log_ext_data(evt_log); + if ( log_ext_data == NULL ) + return log_hashes; + + entry_size = sizeof(*new_entry); + for ( i = 0; i < evt_log->digestCount; ++i ) { + entry_size += sizeof(uint16_t); /* hash type */ + entry_size += evt_log->digestSizes[i].digestSize; + } + entry_size += sizeof(uint32_t); /* data size field */ + entry_size += data_size; + + /* + * Check if there is enough space left for new entry. + * Note: it is possible to introduce a gap in event log if entry with big + * data_size is followed by another entry with smaller data. Maybe we should + * cap the event log size in such case? + */ + if ( log_ext_data->next_record_offset + entry_size > evt_log_size ) + return log_hashes; + + new_entry = (void *)((uint8_t *)evt_log + log_ext_data->next_record_offset); + log_ext_data->next_record_offset += entry_size; + + new_entry->pcrIndex = pcr; + new_entry->eventType = type; + new_entry->digestCount = evt_log->digestCount; + + p = &new_entry->digests[0]; + for ( i = 0; i < evt_log->digestCount; ++i ) { + uint16_t alg = evt_log->digestSizes[i].algId; + uint16_t size = evt_log->digestSizes[i].digestSize; + + *(uint16_t *)p = alg; + p += sizeof(uint16_t); + + log_hashes.hashes[i].alg = alg; + log_hashes.hashes[i].size = size; + log_hashes.hashes[i].data = p; + p += size; + + /* This is called "OneDigest" in TXT Software Development Guide. */ + memset(log_hashes.hashes[i].data, 0, size); + log_hashes.hashes[i].data[0] = 1; + } + log_hashes.count = evt_log->digestCount; + + *(uint32_t *)p = data_size; + p += sizeof(uint32_t); + + if ( data && data_size > 0 ) + memcpy(p, data, data_size); + + return log_hashes; +} + +/************************** end of TPM2.0 specific ****************************/ + +void tpm_hash_extend(unsigned loc, unsigned pcr, uint8_t *buf, unsigned size, + uint32_t type, uint8_t *log_data, unsigned log_data_size) +{ + void *evt_log_addr; + uint32_t evt_log_size; + + find_evt_log(__va(slaunch_slrt), &evt_log_addr, &evt_log_size); + evt_log_addr = __va((uintptr_t)evt_log_addr); + + if ( is_tpm12() ) { + uint8_t sha1_digest[SHA1_DIGEST_SIZE]; + + struct txt_ev_log_container_12 *evt_log = evt_log_addr; + void *entry_digest = create_log_event12(evt_log, evt_log_size, pcr, + type, log_data, log_data_size); + + /* We still need to write computed hash somewhere. */ + if ( entry_digest == NULL ) + entry_digest = sha1_digest; + + if ( !tpm12_hash_extend(loc, buf, size, pcr, entry_digest) ) { +#ifndef __EARLY_TPM__ + printk(XENLOG_ERR "Extending PCR%u failed\n", pcr); +#endif + } + } else { + uint32_t rc; + + struct tpm2_spec_id_event *evt_log = evt_log_addr; + struct tpm2_log_hashes log_hashes = + create_log_event20(evt_log, evt_log_size, pcr, type, log_data, + log_data_size); + + rc = tpm2_hash_extend(loc, buf, size, pcr, &log_hashes); + if ( rc != 0 ) { +#ifndef __EARLY_TPM__ + printk(XENLOG_ERR "Extending PCR%u failed with TPM error: 0x%08x\n", + pcr, rc); +#endif + } + } +} + +#ifdef __EARLY_TPM__ +void tpm_extend_mbi(uint32_t *mbi, uint32_t slrt_pa) +{ + /* Early TPM code isn't linked with the rest but still needs to have this + * variable with correct value. */ + slaunch_slrt = slrt_pa; + + /* MBI starts with uint32_t total_size. */ + tpm_hash_extend(DRTM_LOC, DRTM_DATA_PCR, (uint8_t *)mbi, *mbi, + DLE_EVTYPE_SLAUNCH, NULL, 0); +} +#endif diff --git a/xen/arch/x86/x86_64/asm-offsets.c b/xen/arch/x86/x86_64/asm-offsets.c index 630bdc3945..93c490bd04 100644 --- a/xen/arch/x86/x86_64/asm-offsets.c +++ b/xen/arch/x86/x86_64/asm-offsets.c @@ -191,7 +191,9 @@ void __dummy__(void) OFFSET(IRQSTAT_softirq_pending, irq_cpustat_t, __softirq_pending); BLANK(); - OFFSET(CPUINFO_features, struct cpuinfo_x86, x86_capability); + OFFSET(CPUINFO_X86_features, struct cpuinfo_x86, x86_capability); + OFFSET(CPUINFO_X86_apicid, struct cpuinfo_x86, apicid); + DEFINE(CPUINFO_X86_sizeof, sizeof(struct cpuinfo_x86)); BLANK(); OFFSET(MB_flags, multiboot_info_t, flags); diff --git a/xen/arch/x86/x86_64/platform_hypercall.c b/xen/arch/x86/x86_64/platform_hypercall.c index 347f5d6533..9ab631c17f 100644 --- a/xen/arch/x86/x86_64/platform_hypercall.c +++ b/xen/arch/x86/x86_64/platform_hypercall.c @@ -4,8 +4,8 @@ EMIT_FILE; -#include #include +#include #define xen_platform_op compat_platform_op #define xen_platform_op_t compat_platform_op_t diff --git a/xen/common/device-tree/bootfdt.c b/xen/common/device-tree/bootfdt.c index 927f59c64b..480644b4b4 100644 --- a/xen/common/device-tree/bootfdt.c +++ b/xen/common/device-tree/bootfdt.c @@ -439,7 +439,7 @@ static int __init process_chosen_node(const void *fdt, int node, return -EINVAL; } - printk("Initrd %"PRIpaddr"-%"PRIpaddr"\n", start, end); + printk("Initrd %"PRIpaddr"-%"PRIpaddr"\n", start, end - 1); add_boot_module(BOOTMOD_RAMDISK, start, end-start, false); @@ -524,7 +524,7 @@ static void __init early_print_info(void) printk("MODULE[%d]: %"PRIpaddr" - %"PRIpaddr" %-12s\n", i, mods->module[i].start, - mods->module[i].start + mods->module[i].size, + mods->module[i].start + mods->module[i].size - 1, boot_module_kind_as_string(mods->module[i].kind)); for ( i = 0; i < mem_resv->nr_banks; i++ ) diff --git a/xen/common/device-tree/bootinfo.c b/xen/common/device-tree/bootinfo.c index f2e6a1145b..3738eb57ff 100644 --- a/xen/common/device-tree/bootinfo.c +++ b/xen/common/device-tree/bootinfo.c @@ -17,6 +17,8 @@ #include #include +#include + struct bootinfo __initdata bootinfo = BOOTINFO_INIT; const char * __init boot_module_kind_as_string(bootmodule_kind kind) diff --git a/xen/include/xen/pmstat.h b/xen/include/xen/pmstat.h index e6ab1423a9..8350403e95 100644 --- a/xen/include/xen/pmstat.h +++ b/xen/include/xen/pmstat.h @@ -7,6 +7,14 @@ int set_px_pminfo(uint32_t acpi_id, struct xen_processor_performance *perf); long set_cx_pminfo(uint32_t acpi_id, struct xen_processor_power *power); + +#ifdef CONFIG_COMPAT +struct compat_processor_performance; +int compat_set_px_pminfo(uint32_t acpi_id, struct compat_processor_performance *perf); +struct compat_processor_power; +long compat_set_cx_pminfo(uint32_t acpi_id, struct compat_processor_power *power); +#endif + uint32_t pmstat_get_cx_nr(unsigned int cpu); int pmstat_get_cx_stat(unsigned int cpu, struct pm_cx_stat *stat); int pmstat_reset_cx_stat(unsigned int cpu); diff --git a/xen/include/xen/sha1.h b/xen/include/xen/sha1.h new file mode 100644 index 0000000000..85be7b3c12 --- /dev/null +++ b/xen/include/xen/sha1.h @@ -0,0 +1,10 @@ +#ifndef __XEN_SHA1_H +#define __XEN_SHA1_H + +#include + +#define SHA1_DIGEST_SIZE 20 + +void sha1_hash(const u8 *data, unsigned int len, u8 *out); + +#endif /* !__XEN_SHA1_H */ diff --git a/xen/include/xen/sha256.h b/xen/include/xen/sha256.h new file mode 100644 index 0000000000..0a483b6fd6 --- /dev/null +++ b/xen/include/xen/sha256.h @@ -0,0 +1,10 @@ +#ifndef __XEN_SHA256_H +#define __XEN_SHA256_H + +#include + +#define SHA256_DIGEST_SIZE 32 + +void sha256_hash(const u8 *data, unsigned int len, u8 *out); + +#endif /* !__XEN_SHA256_H */ diff --git a/xen/include/xen/slr_table.h b/xen/include/xen/slr_table.h new file mode 100644 index 0000000000..46c890c24a --- /dev/null +++ b/xen/include/xen/slr_table.h @@ -0,0 +1,263 @@ +/* SPDX-License-Identifier: GPL-3.0 */ + +/* + * Copyright (C) 2023 Oracle and/or its affiliates. + * + * Secure Launch Resource Table definitions + */ + +#ifndef _SLR_TABLE_H +#define _SLR_TABLE_H + +#define UEFI_SLR_TABLE_GUID \ + { 0x877a9b2a, 0x0385, 0x45d1, { 0xa0, 0x34, 0x9d, 0xac, 0x9c, 0x9e, 0x56, 0x5f }} + +/* SLR table header values */ +#define SLR_TABLE_MAGIC 0x4452544d +#define SLR_TABLE_REVISION 1 + +/* Current revisions for the policy and UEFI config */ +#define SLR_POLICY_REVISION 1 +#define SLR_UEFI_CONFIG_REVISION 1 + +/* SLR defined architectures */ +#define SLR_INTEL_TXT 1 +#define SLR_AMD_SKINIT 2 + +/* SLR defined bootloaders */ +#define SLR_BOOTLOADER_INVALID 0 +#define SLR_BOOTLOADER_GRUB 1 + +/* Log formats */ +#define SLR_DRTM_TPM12_LOG 1 +#define SLR_DRTM_TPM20_LOG 2 + +/* DRTM Policy Entry Flags */ +#define SLR_POLICY_FLAG_MEASURED 0x1 +#define SLR_POLICY_IMPLICIT_SIZE 0x2 + +/* Array Lengths */ +#define TPM_EVENT_INFO_LENGTH 32 +#define TXT_VARIABLE_MTRRS_LENGTH 32 + +/* Tags */ +#define SLR_ENTRY_INVALID 0x0000 +#define SLR_ENTRY_DL_INFO 0x0001 +#define SLR_ENTRY_LOG_INFO 0x0002 +#define SLR_ENTRY_DRTM_POLICY 0x0003 +#define SLR_ENTRY_INTEL_INFO 0x0004 +#define SLR_ENTRY_AMD_INFO 0x0005 +#define SLR_ENTRY_ARM_INFO 0x0006 +#define SLR_ENTRY_UEFI_INFO 0x0007 +#define SLR_ENTRY_UEFI_CONFIG 0x0008 +#define SLR_ENTRY_END 0xffff + +/* Entity Types */ +#define SLR_ET_UNSPECIFIED 0x0000 +#define SLR_ET_SLRT 0x0001 +#define SLR_ET_BOOT_PARAMS 0x0002 +#define SLR_ET_SETUP_DATA 0x0003 +#define SLR_ET_CMDLINE 0x0004 +#define SLR_ET_UEFI_MEMMAP 0x0005 +#define SLR_ET_RAMDISK 0x0006 +#define SLR_ET_MULTIBOOT2_INFO 0x0007 +#define SLR_ET_MULTIBOOT2_MODULE 0x0008 +#define SLR_ET_TXT_OS2MLE 0x0010 +#define SLR_ET_UNUSED 0xffff + +/* + * Primary SLR Table Header + */ +struct slr_table +{ + uint32_t magic; + uint16_t revision; + uint16_t architecture; + uint32_t size; + uint32_t max_size; + /* entries[] */ +} __packed; + +/* + * Common SLRT Table Header + */ +struct slr_entry_hdr +{ + uint32_t tag; + uint32_t size; +} __packed; + +/* + * Boot loader context + */ +struct slr_bl_context +{ + uint16_t bootloader; + uint16_t reserved[3]; + uint64_t context; +} __packed; + +/* + * DRTM Dynamic Launch Configuration + */ +struct slr_entry_dl_info +{ + struct slr_entry_hdr hdr; + uint64_t dce_size; + uint64_t dce_base; + uint64_t dlme_size; + uint64_t dlme_base; + uint64_t dlme_entry; + struct slr_bl_context bl_context; + uint64_t dl_handler; +} __packed; + +/* + * TPM Log Information + */ +struct slr_entry_log_info +{ + struct slr_entry_hdr hdr; + uint16_t format; + uint16_t reserved; + uint32_t size; + uint64_t addr; +} __packed; + +/* + * DRTM Measurement Entry + */ +struct slr_policy_entry +{ + uint16_t pcr; + uint16_t entity_type; + uint16_t flags; + uint16_t reserved; + uint64_t size; + uint64_t entity; + char evt_info[TPM_EVENT_INFO_LENGTH]; +} __packed; + +/* + * DRTM Measurement Policy + */ +struct slr_entry_policy +{ + struct slr_entry_hdr hdr; + uint16_t reserved[2]; + uint16_t revision; + uint16_t nr_entries; + struct slr_policy_entry policy_entries[]; +} __packed; + +/* + * Secure Launch defined MTRR saving structures + */ +struct slr_txt_mtrr_pair +{ + uint64_t mtrr_physbase; + uint64_t mtrr_physmask; +} __packed; + +struct slr_txt_mtrr_state +{ + uint64_t default_mem_type; + uint64_t mtrr_vcnt; + struct slr_txt_mtrr_pair mtrr_pair[TXT_VARIABLE_MTRRS_LENGTH]; +} __packed; + +/* + * Intel TXT Info table + */ +struct slr_entry_intel_info +{ + struct slr_entry_hdr hdr; + uint64_t txt_heap; + uint64_t saved_misc_enable_msr; + struct slr_txt_mtrr_state saved_bsp_mtrrs; +} __packed; + +/* + * AMD SKINIT Info table + */ +struct slr_entry_amd_info +{ + struct slr_entry_hdr hdr; +} __packed; + +/* + * ARM DRTM Info table + */ +struct slr_entry_arm_info +{ + struct slr_entry_hdr hdr; +} __packed; + +/* + * UEFI config measurement entry + */ +struct slr_uefi_cfg_entry +{ + uint16_t pcr; + uint16_t reserved; + uint32_t size; + uint64_t cfg; /* address or value */ + char evt_info[TPM_EVENT_INFO_LENGTH]; +} __packed; + +struct slr_entry_uefi_config +{ + struct slr_entry_hdr hdr; + uint16_t reserved[2]; + uint16_t revision; + uint16_t nr_entries; + struct slr_uefi_cfg_entry uefi_cfg_entries[]; +} __packed; + +static inline void * +slr_end_of_entries(struct slr_table *table) +{ + return (uint8_t *)table + table->size; +} + +static inline struct slr_entry_hdr * +slr_next_entry(struct slr_table *table, struct slr_entry_hdr *curr) +{ + struct slr_entry_hdr *next = (struct slr_entry_hdr *) + ((uint8_t *)curr + curr->size); + + if ( (void *)next >= slr_end_of_entries(table) ) + return NULL; + if ( next->tag == SLR_ENTRY_END ) + return NULL; + + return next; +} + +static inline struct slr_entry_hdr * +slr_next_entry_by_tag (struct slr_table *table, + struct slr_entry_hdr *entry, + uint16_t tag) +{ + if ( !entry ) /* Start from the beginning */ + entry = (struct slr_entry_hdr *)((uint8_t *)table + sizeof(*table)); + + for ( ; ; ) + { + if ( entry->tag == tag ) + return entry; + + entry = slr_next_entry(table, entry); + if ( !entry ) + return NULL; + } + + return NULL; +} + +/* + * slr_add_entry() and slr_init_table() were omitted to not have issues with + * memcpy() usage. + */ + +#endif /* _SLR_TABLE_H */ diff --git a/xen/lib/Makefile b/xen/lib/Makefile index 54440f628a..40737c05cd 100644 --- a/xen/lib/Makefile +++ b/xen/lib/Makefile @@ -37,6 +37,8 @@ lib-y += strtoll.o lib-y += strtoul.o lib-y += strtoull.o lib-$(CONFIG_X86) += x86-generic-hweightl.o +lib-$(CONFIG_X86) += sha1.o +lib-$(CONFIG_X86) += sha256.o lib-$(CONFIG_X86) += xxhash32.o lib-$(CONFIG_X86) += xxhash64.o diff --git a/xen/lib/sha1.c b/xen/lib/sha1.c new file mode 100644 index 0000000000..a11822519d --- /dev/null +++ b/xen/lib/sha1.c @@ -0,0 +1,240 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * SHA1 routine optimized to do word accesses rather than byte accesses, + * and to avoid unnecessary copies into the context array. + * + * This was based on the git SHA1 implementation. + */ + +#include +#include +#include +#include + +/* + * If you have 32 registers or more, the compiler can (and should) + * try to change the array[] accesses into registers. However, on + * machines with less than ~25 registers, that won't really work, + * and at least gcc will make an unholy mess of it. + * + * So to avoid that mess which just slows things down, we force + * the stores to memory to actually happen (we might be better off + * with a 'W(t)=(val);asm("":"+m" (W(t))' there instead, as + * suggested by Artur Skawina - that will also make gcc unable to + * try to do the silly "optimize away loads" part because it won't + * see what the value will be). + * + * Ben Herrenschmidt reports that on PPC, the C version comes close + * to the optimized asm with this (ie on PPC you don't want that + * 'volatile', since there are lots of registers). + * + * On ARM we get the best code generation by forcing a full memory barrier + * between each SHA_ROUND, otherwise gcc happily get wild with spilling and + * the stack frame size simply explode and performance goes down the drain. + */ + +#ifdef CONFIG_X86 + #define setW(x, val) (*(volatile uint32_t *)&W(x) = (val)) +#elif defined(CONFIG_ARM) + #define setW(x, val) do { W(x) = (val); __asm__("":::"memory"); } while ( 0 ) +#else + #define setW(x, val) (W(x) = (val)) +#endif + +/* This "rolls" over the 512-bit array */ +#define W(x) (array[(x) & 15]) + +/* + * Where do we get the source from? The first 16 iterations get it from + * the input data, the next mix it from the 512-bit array. + */ +#define SHA_SRC(t) get_unaligned_be32((uint32_t *)data + t) +#define SHA_MIX(t) rol32(W(t + 13) ^ W(t + 8) ^ W(t + 2) ^ W(t), 1) + +#define SHA_ROUND(t, input, fn, constant, A, B, C, D, E) do { \ + uint32_t TEMP = input(t); setW(t, TEMP); \ + E += TEMP + rol32(A, 5) + (fn) + (constant); \ + B = ror32(B, 2); \ + TEMP = E; E = D; D = C; C = B; B = A; A = TEMP; \ + } while ( 0 ) + +#define T_0_15(t, A, B, C, D, E) \ + SHA_ROUND(t, SHA_SRC, (((C ^ D) & B) ^ D), 0x5a827999, A, B, C, D, E) +#define T_16_19(t, A, B, C, D, E) \ + SHA_ROUND(t, SHA_MIX, (((C ^ D) & B) ^ D), 0x5a827999, A, B, C, D, E) +#define T_20_39(t, A, B, C, D, E) \ + SHA_ROUND(t, SHA_MIX, (B ^ C ^ D), 0x6ed9eba1, A, B, C, D, E) +#define T_40_59(t, A, B, C, D, E) \ + SHA_ROUND(t, SHA_MIX, ((B & C) + (D & (B ^ C))), 0x8f1bbcdc, A, B, C, \ + D, E) +#define T_60_79(t, A, B, C, D, E) \ + SHA_ROUND(t, SHA_MIX, (B ^ C ^ D), 0xca62c1d6, A, B, C, D, E) + +#define SHA1_BLOCK_SIZE 64 +#define SHA1_WORKSPACE_WORDS 16 + +struct sha1_state { + uint32_t state[SHA1_DIGEST_SIZE / 4]; + uint64_t count; + uint8_t buffer[SHA1_BLOCK_SIZE]; +}; + +typedef void sha1_block_fn(struct sha1_state *sst, const uint8_t *src, int blocks); + +/** + * sha1_transform - single block SHA1 transform (deprecated) + * + * @digest: 160 bit digest to update + * @data: 512 bits of data to hash + * @array: 16 words of workspace (see note) + * + * This function executes SHA-1's internal compression function. It updates the + * 160-bit internal state (@digest) with a single 512-bit data block (@data). + * + * Don't use this function. SHA-1 is no longer considered secure. And even if + * you do have to use SHA-1, this isn't the correct way to hash something with + * SHA-1 as this doesn't handle padding and finalization. + * + * Note: If the hash is security sensitive, the caller should be sure + * to clear the workspace. This is left to the caller to avoid + * unnecessary clears between chained hashing operations. + */ +void sha1_transform(uint32_t *digest, const uint8_t *data, uint32_t *array) +{ + uint32_t A, B, C, D, E; + unsigned int i = 0; + + A = digest[0]; + B = digest[1]; + C = digest[2]; + D = digest[3]; + E = digest[4]; + + /* Round 1 - iterations 0-16 take their input from 'data' */ + for ( ; i < 16; ++i ) + T_0_15(i, A, B, C, D, E); + + /* Round 1 - tail. Input from 512-bit mixing array */ + for ( ; i < 20; ++i ) + T_16_19(i, A, B, C, D, E); + + /* Round 2 */ + for ( ; i < 40; ++i ) + T_20_39(i, A, B, C, D, E); + + /* Round 3 */ + for ( ; i < 60; ++i ) + T_40_59(i, A, B, C, D, E); + + /* Round 4 */ + for ( ; i < 80; ++i ) + T_60_79(i, A, B, C, D, E); + + digest[0] += A; + digest[1] += B; + digest[2] += C; + digest[3] += D; + digest[4] += E; +} + +static void sha1_init(struct sha1_state *sctx) +{ + sctx->state[0] = 0x67452301UL; + sctx->state[1] = 0xefcdab89UL; + sctx->state[2] = 0x98badcfeUL; + sctx->state[3] = 0x10325476UL; + sctx->state[4] = 0xc3d2e1f0UL; + sctx->count = 0; +} + +static void sha1_do_update(struct sha1_state *sctx, + const uint8_t *data, + unsigned int len, + sha1_block_fn *block_fn) +{ + unsigned int partial = sctx->count % SHA1_BLOCK_SIZE; + + sctx->count += len; + + if ( unlikely((partial + len) >= SHA1_BLOCK_SIZE) ) + { + int blocks; + + if ( partial ) + { + int p = SHA1_BLOCK_SIZE - partial; + + memcpy(sctx->buffer + partial, data, p); + data += p; + len -= p; + + block_fn(sctx, sctx->buffer, 1); + } + + blocks = len / SHA1_BLOCK_SIZE; + len %= SHA1_BLOCK_SIZE; + + if ( blocks ) + { + block_fn(sctx, data, blocks); + data += blocks * SHA1_BLOCK_SIZE; + } + partial = 0; + } + if ( len ) + memcpy(sctx->buffer + partial, data, len); +} + +static void sha1_do_finalize(struct sha1_state *sctx, sha1_block_fn *block_fn) +{ + const int bit_offset = SHA1_BLOCK_SIZE - sizeof(__be64); + __be64 *bits = (__be64 *)(sctx->buffer + bit_offset); + unsigned int partial = sctx->count % SHA1_BLOCK_SIZE; + + sctx->buffer[partial++] = 0x80; + if ( partial > bit_offset ) + { + memset(sctx->buffer + partial, 0x0, SHA1_BLOCK_SIZE - partial); + partial = 0; + + block_fn(sctx, sctx->buffer, 1); + } + + memset(sctx->buffer + partial, 0x0, bit_offset - partial); + *bits = cpu_to_be64(sctx->count << 3); + block_fn(sctx, sctx->buffer, 1); +} + +static void sha1_finish(struct sha1_state *sctx, uint8_t *out) +{ + __be32 *digest = (__be32 *)out; + int i; + + for ( i = 0; i < SHA1_DIGEST_SIZE / sizeof(__be32); i++ ) + put_unaligned_be32(sctx->state[i], digest++); + + memset(sctx, 0, sizeof(*sctx)); +} + +static void sha1_generic_block_fn(struct sha1_state *sctx, const uint8_t *src, + int blocks) +{ + uint32_t temp[SHA1_WORKSPACE_WORDS]; + + while ( blocks-- ) + { + sha1_transform(sctx->state, src, temp); + src += SHA1_BLOCK_SIZE; + } + memset(temp, 0, sizeof(temp)); +} + +void sha1_hash(const uint8_t *data, unsigned int len, uint8_t *out) +{ + struct sha1_state sctx; + + sha1_init(&sctx); + sha1_do_update(&sctx, data, len, sha1_generic_block_fn); + sha1_do_finalize(&sctx, sha1_generic_block_fn); + sha1_finish(&sctx, out); +} diff --git a/xen/lib/sha256.c b/xen/lib/sha256.c new file mode 100644 index 0000000000..d569efa88c --- /dev/null +++ b/xen/lib/sha256.c @@ -0,0 +1,238 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * SHA-256, as specified in + * http://csrc.nist.gov/groups/STM/cavp/documents/shs/sha256-384-512.pdf + * + * SHA-256 code by Jean-Luc Cooke . + * + * Copyright (c) Jean-Luc Cooke + * Copyright (c) Andrew McDonald + * Copyright (c) 2002 James Morris + * Copyright (c) 2014 Red Hat Inc. + */ + +#include +#include +#include + +#define SHA256_BLOCK_SIZE 64 + +struct sha256_state { + uint32_t state[SHA256_DIGEST_SIZE / 4]; + uint64_t count; + uint8_t buf[SHA256_BLOCK_SIZE]; +}; + +typedef void sha256_block_fn(struct sha256_state *sst, uint8_t const *src, + int blocks); + +static const uint32_t SHA256_K[] = { + 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, + 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, + 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, + 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, + 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, + 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, + 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, + 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, + 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, + 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, + 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, + 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, + 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, + 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, + 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, + 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2, +}; + +static uint32_t Ch(uint32_t x, uint32_t y, uint32_t z) +{ + return z ^ (x & (y ^ z)); +} + +static uint32_t Maj(uint32_t x, uint32_t y, uint32_t z) +{ + return (x & y) | (z & (x | y)); +} + +#define e0(x) (ror32(x, 2) ^ ror32(x, 13) ^ ror32(x, 22)) +#define e1(x) (ror32(x, 6) ^ ror32(x, 11) ^ ror32(x, 25)) +#define s0(x) (ror32(x, 7) ^ ror32(x, 18) ^ (x >> 3)) +#define s1(x) (ror32(x, 17) ^ ror32(x, 19) ^ (x >> 10)) + +static void LOAD_OP(int I, uint32_t *W, const uint8_t *input) +{ + W[I] = get_unaligned_be32((uint32_t *)input + I); +} + +static void BLEND_OP(int I, uint32_t *W) +{ + W[I] = s1(W[I - 2]) + W[I - 7] + s0(W[I - 15]) + W[I - 16]; +} + +#define SHA256_ROUND(i, a, b, c, d, e, f, g, h) do { \ + uint32_t t1, t2; \ + t1 = h + e1(e) + Ch(e, f, g) + SHA256_K[i] + W[i]; \ + t2 = e0(a) + Maj(a, b, c); \ + d += t1; \ + h = t1 + t2; \ + } while ( 0 ) + +static void sha256_init(struct sha256_state *sctx) +{ + sctx->state[0] = 0x6a09e667UL; + sctx->state[1] = 0xbb67ae85UL; + sctx->state[2] = 0x3c6ef372UL; + sctx->state[3] = 0xa54ff53aUL; + sctx->state[4] = 0x510e527fUL; + sctx->state[5] = 0x9b05688cUL; + sctx->state[6] = 0x1f83d9abUL; + sctx->state[7] = 0x5be0cd19UL; + sctx->count = 0; +} + +static void sha256_do_update(struct sha256_state *sctx, + const uint8_t *data, + unsigned int len, + sha256_block_fn *block_fn) +{ + unsigned int partial = sctx->count % SHA256_BLOCK_SIZE; + + sctx->count += len; + + if ( unlikely((partial + len) >= SHA256_BLOCK_SIZE) ) + { + int blocks; + + if ( partial ) + { + int p = SHA256_BLOCK_SIZE - partial; + + memcpy(sctx->buf + partial, data, p); + data += p; + len -= p; + + block_fn(sctx, sctx->buf, 1); + } + + blocks = len / SHA256_BLOCK_SIZE; + len %= SHA256_BLOCK_SIZE; + + if ( blocks ) + { + block_fn(sctx, data, blocks); + data += blocks * SHA256_BLOCK_SIZE; + } + partial = 0; + } + if ( len ) + memcpy(sctx->buf + partial, data, len); +} + +static void sha256_do_finalize(struct sha256_state *sctx, + sha256_block_fn *block_fn) +{ + const int bit_offset = SHA256_BLOCK_SIZE - sizeof(__be64); + __be64 *bits = (__be64 *)(sctx->buf + bit_offset); + unsigned int partial = sctx->count % SHA256_BLOCK_SIZE; + + sctx->buf[partial++] = 0x80; + if ( partial > bit_offset ) + { + memset(sctx->buf + partial, 0x0, SHA256_BLOCK_SIZE - partial); + partial = 0; + + block_fn(sctx, sctx->buf, 1); + } + + memset(sctx->buf + partial, 0x0, bit_offset - partial); + *bits = cpu_to_be64(sctx->count << 3); + block_fn(sctx, sctx->buf, 1); +} + +static void sha256_finish(struct sha256_state *sctx, uint8_t *out, + unsigned int digest_size) +{ + __be32 *digest = (__be32 *)out; + int i; + + for ( i = 0; digest_size > 0; i++, digest_size -= sizeof(__be32) ) + put_unaligned_be32(sctx->state[i], digest++); + + memset(sctx, 0, sizeof(*sctx)); +} + +static void sha256_transform(uint32_t *state, const uint8_t *input, uint32_t *W) +{ + uint32_t a, b, c, d, e, f, g, h; + int i; + + /* load the input */ + for ( i = 0; i < 16; i += 8 ) + { + LOAD_OP(i + 0, W, input); + LOAD_OP(i + 1, W, input); + LOAD_OP(i + 2, W, input); + LOAD_OP(i + 3, W, input); + LOAD_OP(i + 4, W, input); + LOAD_OP(i + 5, W, input); + LOAD_OP(i + 6, W, input); + LOAD_OP(i + 7, W, input); + } + + /* now blend */ + for ( i = 16; i < 64; i += 8 ) + { + BLEND_OP(i + 0, W); + BLEND_OP(i + 1, W); + BLEND_OP(i + 2, W); + BLEND_OP(i + 3, W); + BLEND_OP(i + 4, W); + BLEND_OP(i + 5, W); + BLEND_OP(i + 6, W); + BLEND_OP(i + 7, W); + } + + /* 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 < 64; i += 8 ) + { + SHA256_ROUND(i + 0, a, b, c, d, e, f, g, h); + SHA256_ROUND(i + 1, h, a, b, c, d, e, f, g); + SHA256_ROUND(i + 2, g, h, a, b, c, d, e, f); + SHA256_ROUND(i + 3, f, g, h, a, b, c, d, e); + SHA256_ROUND(i + 4, e, f, g, h, a, b, c, d); + SHA256_ROUND(i + 5, d, e, f, g, h, a, b, c); + SHA256_ROUND(i + 6, c, d, e, f, g, h, a, b); + SHA256_ROUND(i + 7, b, c, d, e, f, g, h, a); + } + + state[0] += a; state[1] += b; state[2] += c; state[3] += d; + state[4] += e; state[5] += f; state[6] += g; state[7] += h; +} + +static void sha256_transform_blocks(struct sha256_state *sctx, + const uint8_t *input, int blocks) +{ + uint32_t W[64]; + + do { + sha256_transform(sctx->state, input, W); + input += SHA256_BLOCK_SIZE; + } while ( --blocks ); + + memset(W, 0, sizeof(W)); +} + +void sha256_hash(const uint8_t *data, unsigned int len, uint8_t *out) +{ + struct sha256_state sctx; + + sha256_init(&sctx); + sha256_do_update(&sctx, data, len, sha256_transform_blocks); + sha256_do_finalize(&sctx, sha256_transform_blocks); + sha256_finish(&sctx, out, SHA256_DIGEST_SIZE); +} diff --git a/xen/xsm/xsm_policy.c b/xen/xsm/xsm_policy.c index 76280903d5..7f70d860bd 100644 --- a/xen/xsm/xsm_policy.c +++ b/xen/xsm/xsm_policy.c @@ -43,7 +43,7 @@ int __init xsm_multiboot_policy_init( struct boot_module *bm = &bi->mods[i]; _policy_start = bootstrap_map_bm(bm); - _policy_len = bm->mod->mod_end; + _policy_len = bm->size; if ( (xsm_magic_t)(*_policy_start) == XSM_MAGIC ) {