18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci#include <linux/init.h>
38c2ecf20Sopenharmony_ci#include <linux/ctype.h>
48c2ecf20Sopenharmony_ci#include <asm/ebcdic.h>
58c2ecf20Sopenharmony_ci#include <asm/sclp.h>
68c2ecf20Sopenharmony_ci#include <asm/sections.h>
78c2ecf20Sopenharmony_ci#include <asm/boot_data.h>
88c2ecf20Sopenharmony_ci#include <uapi/asm/ipl.h>
98c2ecf20Sopenharmony_ci#include "boot.h"
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ciint __bootdata_preserved(ipl_secure_flag);
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ciunsigned long __bootdata_preserved(ipl_cert_list_addr);
148c2ecf20Sopenharmony_ciunsigned long __bootdata_preserved(ipl_cert_list_size);
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ciunsigned long __bootdata(early_ipl_comp_list_addr);
178c2ecf20Sopenharmony_ciunsigned long __bootdata(early_ipl_comp_list_size);
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci#define for_each_rb_entry(entry, rb) \
208c2ecf20Sopenharmony_ci	for (entry = rb->entries; \
218c2ecf20Sopenharmony_ci	     (void *) entry + sizeof(*entry) <= (void *) rb + rb->len; \
228c2ecf20Sopenharmony_ci	     entry++)
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_cistatic inline bool intersects(unsigned long addr0, unsigned long size0,
258c2ecf20Sopenharmony_ci			      unsigned long addr1, unsigned long size1)
268c2ecf20Sopenharmony_ci{
278c2ecf20Sopenharmony_ci	return addr0 + size0 > addr1 && addr1 + size1 > addr0;
288c2ecf20Sopenharmony_ci}
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_cistatic unsigned long find_bootdata_space(struct ipl_rb_components *comps,
318c2ecf20Sopenharmony_ci					 struct ipl_rb_certificates *certs,
328c2ecf20Sopenharmony_ci					 unsigned long safe_addr)
338c2ecf20Sopenharmony_ci{
348c2ecf20Sopenharmony_ci	struct ipl_rb_certificate_entry *cert;
358c2ecf20Sopenharmony_ci	struct ipl_rb_component_entry *comp;
368c2ecf20Sopenharmony_ci	size_t size;
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci	/*
398c2ecf20Sopenharmony_ci	 * Find the length for the IPL report boot data
408c2ecf20Sopenharmony_ci	 */
418c2ecf20Sopenharmony_ci	early_ipl_comp_list_size = 0;
428c2ecf20Sopenharmony_ci	for_each_rb_entry(comp, comps)
438c2ecf20Sopenharmony_ci		early_ipl_comp_list_size += sizeof(*comp);
448c2ecf20Sopenharmony_ci	ipl_cert_list_size = 0;
458c2ecf20Sopenharmony_ci	for_each_rb_entry(cert, certs)
468c2ecf20Sopenharmony_ci		ipl_cert_list_size += sizeof(unsigned int) + cert->len;
478c2ecf20Sopenharmony_ci	size = ipl_cert_list_size + early_ipl_comp_list_size;
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci	/*
508c2ecf20Sopenharmony_ci	 * Start from safe_addr to find a free memory area large
518c2ecf20Sopenharmony_ci	 * enough for the IPL report boot data. This area is used
528c2ecf20Sopenharmony_ci	 * for ipl_cert_list_addr/ipl_cert_list_size and
538c2ecf20Sopenharmony_ci	 * early_ipl_comp_list_addr/early_ipl_comp_list_size. It must
548c2ecf20Sopenharmony_ci	 * not overlap with any component or any certificate.
558c2ecf20Sopenharmony_ci	 */
568c2ecf20Sopenharmony_cirepeat:
578c2ecf20Sopenharmony_ci	if (IS_ENABLED(CONFIG_BLK_DEV_INITRD) && INITRD_START && INITRD_SIZE &&
588c2ecf20Sopenharmony_ci	    intersects(INITRD_START, INITRD_SIZE, safe_addr, size))
598c2ecf20Sopenharmony_ci		safe_addr = INITRD_START + INITRD_SIZE;
608c2ecf20Sopenharmony_ci	if (intersects(safe_addr, size, (unsigned long)comps, comps->len)) {
618c2ecf20Sopenharmony_ci		safe_addr = (unsigned long)comps + comps->len;
628c2ecf20Sopenharmony_ci		goto repeat;
638c2ecf20Sopenharmony_ci	}
648c2ecf20Sopenharmony_ci	for_each_rb_entry(comp, comps)
658c2ecf20Sopenharmony_ci		if (intersects(safe_addr, size, comp->addr, comp->len)) {
668c2ecf20Sopenharmony_ci			safe_addr = comp->addr + comp->len;
678c2ecf20Sopenharmony_ci			goto repeat;
688c2ecf20Sopenharmony_ci		}
698c2ecf20Sopenharmony_ci	if (intersects(safe_addr, size, (unsigned long)certs, certs->len)) {
708c2ecf20Sopenharmony_ci		safe_addr = (unsigned long)certs + certs->len;
718c2ecf20Sopenharmony_ci		goto repeat;
728c2ecf20Sopenharmony_ci	}
738c2ecf20Sopenharmony_ci	for_each_rb_entry(cert, certs)
748c2ecf20Sopenharmony_ci		if (intersects(safe_addr, size, cert->addr, cert->len)) {
758c2ecf20Sopenharmony_ci			safe_addr = cert->addr + cert->len;
768c2ecf20Sopenharmony_ci			goto repeat;
778c2ecf20Sopenharmony_ci		}
788c2ecf20Sopenharmony_ci	early_ipl_comp_list_addr = safe_addr;
798c2ecf20Sopenharmony_ci	ipl_cert_list_addr = safe_addr + early_ipl_comp_list_size;
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	return safe_addr + size;
828c2ecf20Sopenharmony_ci}
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_cistatic void copy_components_bootdata(struct ipl_rb_components *comps)
858c2ecf20Sopenharmony_ci{
868c2ecf20Sopenharmony_ci	struct ipl_rb_component_entry *comp, *ptr;
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci	ptr = (struct ipl_rb_component_entry *) early_ipl_comp_list_addr;
898c2ecf20Sopenharmony_ci	for_each_rb_entry(comp, comps)
908c2ecf20Sopenharmony_ci		memcpy(ptr++, comp, sizeof(*ptr));
918c2ecf20Sopenharmony_ci}
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_cistatic void copy_certificates_bootdata(struct ipl_rb_certificates *certs)
948c2ecf20Sopenharmony_ci{
958c2ecf20Sopenharmony_ci	struct ipl_rb_certificate_entry *cert;
968c2ecf20Sopenharmony_ci	void *ptr;
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	ptr = (void *) ipl_cert_list_addr;
998c2ecf20Sopenharmony_ci	for_each_rb_entry(cert, certs) {
1008c2ecf20Sopenharmony_ci		*(unsigned int *) ptr = cert->len;
1018c2ecf20Sopenharmony_ci		ptr += sizeof(unsigned int);
1028c2ecf20Sopenharmony_ci		memcpy(ptr, (void *) cert->addr, cert->len);
1038c2ecf20Sopenharmony_ci		ptr += cert->len;
1048c2ecf20Sopenharmony_ci	}
1058c2ecf20Sopenharmony_ci}
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ciunsigned long read_ipl_report(unsigned long safe_addr)
1088c2ecf20Sopenharmony_ci{
1098c2ecf20Sopenharmony_ci	struct ipl_rb_certificates *certs;
1108c2ecf20Sopenharmony_ci	struct ipl_rb_components *comps;
1118c2ecf20Sopenharmony_ci	struct ipl_pl_hdr *pl_hdr;
1128c2ecf20Sopenharmony_ci	struct ipl_rl_hdr *rl_hdr;
1138c2ecf20Sopenharmony_ci	struct ipl_rb_hdr *rb_hdr;
1148c2ecf20Sopenharmony_ci	unsigned long tmp;
1158c2ecf20Sopenharmony_ci	void *rl_end;
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci	/*
1188c2ecf20Sopenharmony_ci	 * Check if there is a IPL report by looking at the copy
1198c2ecf20Sopenharmony_ci	 * of the IPL parameter information block.
1208c2ecf20Sopenharmony_ci	 */
1218c2ecf20Sopenharmony_ci	if (!ipl_block_valid ||
1228c2ecf20Sopenharmony_ci	    !(ipl_block.hdr.flags & IPL_PL_FLAG_IPLSR))
1238c2ecf20Sopenharmony_ci		return safe_addr;
1248c2ecf20Sopenharmony_ci	ipl_secure_flag = !!(ipl_block.hdr.flags & IPL_PL_FLAG_SIPL);
1258c2ecf20Sopenharmony_ci	/*
1268c2ecf20Sopenharmony_ci	 * There is an IPL report, to find it load the pointer to the
1278c2ecf20Sopenharmony_ci	 * IPL parameter information block from lowcore and skip past
1288c2ecf20Sopenharmony_ci	 * the IPL parameter list, then align the address to a double
1298c2ecf20Sopenharmony_ci	 * word boundary.
1308c2ecf20Sopenharmony_ci	 */
1318c2ecf20Sopenharmony_ci	tmp = (unsigned long) S390_lowcore.ipl_parmblock_ptr;
1328c2ecf20Sopenharmony_ci	pl_hdr = (struct ipl_pl_hdr *) tmp;
1338c2ecf20Sopenharmony_ci	tmp = (tmp + pl_hdr->len + 7) & -8UL;
1348c2ecf20Sopenharmony_ci	rl_hdr = (struct ipl_rl_hdr *) tmp;
1358c2ecf20Sopenharmony_ci	/* Walk through the IPL report blocks in the IPL Report list */
1368c2ecf20Sopenharmony_ci	certs = NULL;
1378c2ecf20Sopenharmony_ci	comps = NULL;
1388c2ecf20Sopenharmony_ci	rl_end = (void *) rl_hdr + rl_hdr->len;
1398c2ecf20Sopenharmony_ci	rb_hdr = (void *) rl_hdr + sizeof(*rl_hdr);
1408c2ecf20Sopenharmony_ci	while ((void *) rb_hdr + sizeof(*rb_hdr) < rl_end &&
1418c2ecf20Sopenharmony_ci	       (void *) rb_hdr + rb_hdr->len <= rl_end) {
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci		switch (rb_hdr->rbt) {
1448c2ecf20Sopenharmony_ci		case IPL_RBT_CERTIFICATES:
1458c2ecf20Sopenharmony_ci			certs = (struct ipl_rb_certificates *) rb_hdr;
1468c2ecf20Sopenharmony_ci			break;
1478c2ecf20Sopenharmony_ci		case IPL_RBT_COMPONENTS:
1488c2ecf20Sopenharmony_ci			comps = (struct ipl_rb_components *) rb_hdr;
1498c2ecf20Sopenharmony_ci			break;
1508c2ecf20Sopenharmony_ci		default:
1518c2ecf20Sopenharmony_ci			break;
1528c2ecf20Sopenharmony_ci		}
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci		rb_hdr = (void *) rb_hdr + rb_hdr->len;
1558c2ecf20Sopenharmony_ci	}
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	/*
1588c2ecf20Sopenharmony_ci	 * With either the component list or the certificate list
1598c2ecf20Sopenharmony_ci	 * missing the kernel will stay ignorant of secure IPL.
1608c2ecf20Sopenharmony_ci	 */
1618c2ecf20Sopenharmony_ci	if (!comps || !certs)
1628c2ecf20Sopenharmony_ci		return safe_addr;
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	/*
1658c2ecf20Sopenharmony_ci	 * Copy component and certificate list to a safe area
1668c2ecf20Sopenharmony_ci	 * where the decompressed kernel can find them.
1678c2ecf20Sopenharmony_ci	 */
1688c2ecf20Sopenharmony_ci	safe_addr = find_bootdata_space(comps, certs, safe_addr);
1698c2ecf20Sopenharmony_ci	copy_components_bootdata(comps);
1708c2ecf20Sopenharmony_ci	copy_certificates_bootdata(certs);
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci	return safe_addr;
1738c2ecf20Sopenharmony_ci}
174