cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

ipl_report.c (4829B)


      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_data.start && initrd_data.size &&
     58	    intersects(initrd_data.start, initrd_data.size, safe_addr, size))
     59		safe_addr = initrd_data.start + initrd_data.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}