linux/arch/s390/boot/ipl_report.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2#include <linux/init.h>
   3#include <linux/ctype.h>
   4#include <asm/ebcdic.h>
   5#include <asm/sclp.h>
   6#include <asm/sections.h>
   7#include <asm/boot_data.h>
   8#include <uapi/asm/ipl.h>
   9#include "boot.h"
  10
  11int __bootdata_preserved(ipl_secure_flag);
  12
  13unsigned long __bootdata_preserved(ipl_cert_list_addr);
  14unsigned long __bootdata_preserved(ipl_cert_list_size);
  15
  16unsigned long __bootdata(early_ipl_comp_list_addr);
  17unsigned long __bootdata(early_ipl_comp_list_size);
  18
  19#define for_each_rb_entry(entry, rb) \
  20        for (entry = rb->entries; \
  21             (void *) entry + sizeof(*entry) <= (void *) rb + rb->len; \
  22             entry++)
  23
  24static inline bool intersects(unsigned long addr0, unsigned long size0,
  25                              unsigned long addr1, unsigned long size1)
  26{
  27        return addr0 + size0 > addr1 && addr1 + size1 > addr0;
  28}
  29
  30static unsigned long find_bootdata_space(struct ipl_rb_components *comps,
  31                                         struct ipl_rb_certificates *certs,
  32                                         unsigned long safe_addr)
  33{
  34        struct ipl_rb_certificate_entry *cert;
  35        struct ipl_rb_component_entry *comp;
  36        size_t size;
  37
  38        /*
  39         * Find the length for the IPL report boot data
  40         */
  41        early_ipl_comp_list_size = 0;
  42        for_each_rb_entry(comp, comps)
  43                early_ipl_comp_list_size += sizeof(*comp);
  44        ipl_cert_list_size = 0;
  45        for_each_rb_entry(cert, certs)
  46                ipl_cert_list_size += sizeof(unsigned int) + cert->len;
  47        size = ipl_cert_list_size + early_ipl_comp_list_size;
  48
  49        /*
  50         * Start from safe_addr to find a free memory area large
  51         * enough for the IPL report boot data. This area is used
  52         * for ipl_cert_list_addr/ipl_cert_list_size and
  53         * early_ipl_comp_list_addr/early_ipl_comp_list_size. It must
  54         * not overlap with any component or any certificate.
  55         */
  56repeat:
  57        if (IS_ENABLED(CONFIG_BLK_DEV_INITRD) && INITRD_START && INITRD_SIZE &&
  58            intersects(INITRD_START, INITRD_SIZE, safe_addr, size))
  59                safe_addr = INITRD_START + INITRD_SIZE;
  60        for_each_rb_entry(comp, comps)
  61                if (intersects(safe_addr, size, comp->addr, comp->len)) {
  62                        safe_addr = comp->addr + comp->len;
  63                        goto repeat;
  64                }
  65        for_each_rb_entry(cert, certs)
  66                if (intersects(safe_addr, size, cert->addr, cert->len)) {
  67                        safe_addr = cert->addr + cert->len;
  68                        goto repeat;
  69                }
  70        early_ipl_comp_list_addr = safe_addr;
  71        ipl_cert_list_addr = safe_addr + early_ipl_comp_list_size;
  72
  73        return safe_addr + size;
  74}
  75
  76static void copy_components_bootdata(struct ipl_rb_components *comps)
  77{
  78        struct ipl_rb_component_entry *comp, *ptr;
  79
  80        ptr = (struct ipl_rb_component_entry *) early_ipl_comp_list_addr;
  81        for_each_rb_entry(comp, comps)
  82                memcpy(ptr++, comp, sizeof(*ptr));
  83}
  84
  85static void copy_certificates_bootdata(struct ipl_rb_certificates *certs)
  86{
  87        struct ipl_rb_certificate_entry *cert;
  88        void *ptr;
  89
  90        ptr = (void *) ipl_cert_list_addr;
  91        for_each_rb_entry(cert, certs) {
  92                *(unsigned int *) ptr = cert->len;
  93                ptr += sizeof(unsigned int);
  94                memcpy(ptr, (void *) cert->addr, cert->len);
  95                ptr += cert->len;
  96        }
  97}
  98
  99unsigned long read_ipl_report(unsigned long safe_addr)
 100{
 101        struct ipl_rb_certificates *certs;
 102        struct ipl_rb_components *comps;
 103        struct ipl_pl_hdr *pl_hdr;
 104        struct ipl_rl_hdr *rl_hdr;
 105        struct ipl_rb_hdr *rb_hdr;
 106        unsigned long tmp;
 107        void *rl_end;
 108
 109        /*
 110         * Check if there is a IPL report by looking at the copy
 111         * of the IPL parameter information block.
 112         */
 113        if (!ipl_block_valid ||
 114            !(ipl_block.hdr.flags & IPL_PL_FLAG_IPLSR))
 115                return safe_addr;
 116        ipl_secure_flag = !!(ipl_block.hdr.flags & IPL_PL_FLAG_SIPL);
 117        /*
 118         * There is an IPL report, to find it load the pointer to the
 119         * IPL parameter information block from lowcore and skip past
 120         * the IPL parameter list, then align the address to a double
 121         * word boundary.
 122         */
 123        tmp = (unsigned long) S390_lowcore.ipl_parmblock_ptr;
 124        pl_hdr = (struct ipl_pl_hdr *) tmp;
 125        tmp = (tmp + pl_hdr->len + 7) & -8UL;
 126        rl_hdr = (struct ipl_rl_hdr *) tmp;
 127        /* Walk through the IPL report blocks in the IPL Report list */
 128        certs = NULL;
 129        comps = NULL;
 130        rl_end = (void *) rl_hdr + rl_hdr->len;
 131        rb_hdr = (void *) rl_hdr + sizeof(*rl_hdr);
 132        while ((void *) rb_hdr + sizeof(*rb_hdr) < rl_end &&
 133               (void *) rb_hdr + rb_hdr->len <= rl_end) {
 134
 135                switch (rb_hdr->rbt) {
 136                case IPL_RBT_CERTIFICATES:
 137                        certs = (struct ipl_rb_certificates *) rb_hdr;
 138                        break;
 139                case IPL_RBT_COMPONENTS:
 140                        comps = (struct ipl_rb_components *) rb_hdr;
 141                        break;
 142                default:
 143                        break;
 144                }
 145
 146                rb_hdr = (void *) rb_hdr + rb_hdr->len;
 147        }
 148
 149        /*
 150         * With either the component list or the certificate list
 151         * missing the kernel will stay ignorant of secure IPL.
 152         */
 153        if (!comps || !certs)
 154                return safe_addr;
 155
 156        /*
 157         * Copy component and certificate list to a safe area
 158         * where the decompressed kernel can find them.
 159         */
 160        safe_addr = find_bootdata_space(comps, certs, safe_addr);
 161        copy_components_bootdata(comps);
 162        copy_certificates_bootdata(certs);
 163
 164        return safe_addr;
 165}
 166