18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Program to hack in a PT_NOTE program header entry in an ELF file.
48c2ecf20Sopenharmony_ci * This is needed for OF on RS/6000s to load an image correctly.
58c2ecf20Sopenharmony_ci * Note that OF needs a program header entry for the note, not an
68c2ecf20Sopenharmony_ci * ELF section.
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * Copyright 2000 Paul Mackerras.
98c2ecf20Sopenharmony_ci *
108c2ecf20Sopenharmony_ci * Adapted for 64 bit little endian images by Andrew Tauferner.
118c2ecf20Sopenharmony_ci *
128c2ecf20Sopenharmony_ci * Usage: addnote zImage
138c2ecf20Sopenharmony_ci */
148c2ecf20Sopenharmony_ci#include <stdio.h>
158c2ecf20Sopenharmony_ci#include <stdlib.h>
168c2ecf20Sopenharmony_ci#include <fcntl.h>
178c2ecf20Sopenharmony_ci#include <unistd.h>
188c2ecf20Sopenharmony_ci#include <string.h>
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci/* CHRP note section */
218c2ecf20Sopenharmony_cistatic const char arch[] = "PowerPC";
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci#define N_DESCR	6
248c2ecf20Sopenharmony_ciunsigned int descr[N_DESCR] = {
258c2ecf20Sopenharmony_ci	0xffffffff,		/* real-mode = true */
268c2ecf20Sopenharmony_ci	0x02000000,		/* real-base, i.e. where we expect OF to be */
278c2ecf20Sopenharmony_ci	0xffffffff,		/* real-size */
288c2ecf20Sopenharmony_ci	0xffffffff,		/* virt-base */
298c2ecf20Sopenharmony_ci	0xffffffff,		/* virt-size */
308c2ecf20Sopenharmony_ci	0x4000,			/* load-base */
318c2ecf20Sopenharmony_ci};
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci/* RPA note section */
348c2ecf20Sopenharmony_cistatic const char rpaname[] = "IBM,RPA-Client-Config";
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci/*
378c2ecf20Sopenharmony_ci * Note: setting ignore_my_client_config *should* mean that OF ignores
388c2ecf20Sopenharmony_ci * all the other fields, but there is a firmware bug which means that
398c2ecf20Sopenharmony_ci * it looks at the splpar field at least.  So these values need to be
408c2ecf20Sopenharmony_ci * reasonable.
418c2ecf20Sopenharmony_ci */
428c2ecf20Sopenharmony_ci#define N_RPA_DESCR	8
438c2ecf20Sopenharmony_ciunsigned int rpanote[N_RPA_DESCR] = {
448c2ecf20Sopenharmony_ci	0,			/* lparaffinity */
458c2ecf20Sopenharmony_ci	64,			/* min_rmo_size */
468c2ecf20Sopenharmony_ci	0,			/* min_rmo_percent */
478c2ecf20Sopenharmony_ci	40,			/* max_pft_size */
488c2ecf20Sopenharmony_ci	1,			/* splpar */
498c2ecf20Sopenharmony_ci	-1,			/* min_load */
508c2ecf20Sopenharmony_ci	0,			/* new_mem_def */
518c2ecf20Sopenharmony_ci	1,			/* ignore_my_client_config */
528c2ecf20Sopenharmony_ci};
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci#define ROUNDUP(len)	(((len) + 3) & ~3)
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ciunsigned char buf[1024];
578c2ecf20Sopenharmony_ci#define ELFDATA2LSB     1
588c2ecf20Sopenharmony_ci#define ELFDATA2MSB     2
598c2ecf20Sopenharmony_cistatic int e_data = ELFDATA2MSB;
608c2ecf20Sopenharmony_ci#define ELFCLASS32      1
618c2ecf20Sopenharmony_ci#define ELFCLASS64      2
628c2ecf20Sopenharmony_cistatic int e_class = ELFCLASS32;
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci#define GET_16BE(off)	((buf[off] << 8) + (buf[(off)+1]))
658c2ecf20Sopenharmony_ci#define GET_32BE(off)	((GET_16BE(off) << 16U) + GET_16BE((off)+2U))
668c2ecf20Sopenharmony_ci#define GET_64BE(off)	((((unsigned long long)GET_32BE(off)) << 32ULL) + \
678c2ecf20Sopenharmony_ci			((unsigned long long)GET_32BE((off)+4ULL)))
688c2ecf20Sopenharmony_ci#define PUT_16BE(off, v)(buf[off] = ((v) >> 8) & 0xff, \
698c2ecf20Sopenharmony_ci			 buf[(off) + 1] = (v) & 0xff)
708c2ecf20Sopenharmony_ci#define PUT_32BE(off, v)(PUT_16BE((off), (v) >> 16L), PUT_16BE((off) + 2, (v)))
718c2ecf20Sopenharmony_ci#define PUT_64BE(off, v)((PUT_32BE((off), (v) >> 32L), \
728c2ecf20Sopenharmony_ci			  PUT_32BE((off) + 4, (v))))
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci#define GET_16LE(off)	((buf[off]) + (buf[(off)+1] << 8))
758c2ecf20Sopenharmony_ci#define GET_32LE(off)	(GET_16LE(off) + (GET_16LE((off)+2U) << 16U))
768c2ecf20Sopenharmony_ci#define GET_64LE(off)	((unsigned long long)GET_32LE(off) + \
778c2ecf20Sopenharmony_ci			(((unsigned long long)GET_32LE((off)+4ULL)) << 32ULL))
788c2ecf20Sopenharmony_ci#define PUT_16LE(off, v) (buf[off] = (v) & 0xff, \
798c2ecf20Sopenharmony_ci			  buf[(off) + 1] = ((v) >> 8) & 0xff)
808c2ecf20Sopenharmony_ci#define PUT_32LE(off, v) (PUT_16LE((off), (v)), PUT_16LE((off) + 2, (v) >> 16L))
818c2ecf20Sopenharmony_ci#define PUT_64LE(off, v) (PUT_32LE((off), (v)), PUT_32LE((off) + 4, (v) >> 32L))
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci#define GET_16(off)	(e_data == ELFDATA2MSB ? GET_16BE(off) : GET_16LE(off))
848c2ecf20Sopenharmony_ci#define GET_32(off)	(e_data == ELFDATA2MSB ? GET_32BE(off) : GET_32LE(off))
858c2ecf20Sopenharmony_ci#define GET_64(off)	(e_data == ELFDATA2MSB ? GET_64BE(off) : GET_64LE(off))
868c2ecf20Sopenharmony_ci#define PUT_16(off, v)	(e_data == ELFDATA2MSB ? PUT_16BE(off, v) : \
878c2ecf20Sopenharmony_ci			 PUT_16LE(off, v))
888c2ecf20Sopenharmony_ci#define PUT_32(off, v)  (e_data == ELFDATA2MSB ? PUT_32BE(off, v) : \
898c2ecf20Sopenharmony_ci			 PUT_32LE(off, v))
908c2ecf20Sopenharmony_ci#define PUT_64(off, v)  (e_data == ELFDATA2MSB ? PUT_64BE(off, v) : \
918c2ecf20Sopenharmony_ci			 PUT_64LE(off, v))
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci/* Structure of an ELF file */
948c2ecf20Sopenharmony_ci#define E_IDENT		0	/* ELF header */
958c2ecf20Sopenharmony_ci#define	E_PHOFF		(e_class == ELFCLASS32 ? 28 : 32)
968c2ecf20Sopenharmony_ci#define E_PHENTSIZE	(e_class == ELFCLASS32 ? 42 : 54)
978c2ecf20Sopenharmony_ci#define E_PHNUM		(e_class == ELFCLASS32 ? 44 : 56)
988c2ecf20Sopenharmony_ci#define E_HSIZE		(e_class == ELFCLASS32 ? 52 : 64)
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci#define EI_MAGIC	0	/* offsets in E_IDENT area */
1018c2ecf20Sopenharmony_ci#define EI_CLASS	4
1028c2ecf20Sopenharmony_ci#define EI_DATA		5
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci#define PH_TYPE		0	/* ELF program header */
1058c2ecf20Sopenharmony_ci#define PH_OFFSET	(e_class == ELFCLASS32 ? 4 : 8)
1068c2ecf20Sopenharmony_ci#define PH_FILESZ	(e_class == ELFCLASS32 ? 16 : 32)
1078c2ecf20Sopenharmony_ci#define PH_HSIZE	(e_class == ELFCLASS32 ? 32 : 56)
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci#define PT_NOTE		4	/* Program header type = note */
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ciunsigned char elf_magic[4] = { 0x7f, 'E', 'L', 'F' };
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ciint
1158c2ecf20Sopenharmony_cimain(int ac, char **av)
1168c2ecf20Sopenharmony_ci{
1178c2ecf20Sopenharmony_ci	int fd, n, i;
1188c2ecf20Sopenharmony_ci	unsigned long ph, ps, np;
1198c2ecf20Sopenharmony_ci	long nnote, nnote2, ns;
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci	if (ac != 2) {
1228c2ecf20Sopenharmony_ci		fprintf(stderr, "Usage: %s elf-file\n", av[0]);
1238c2ecf20Sopenharmony_ci		exit(1);
1248c2ecf20Sopenharmony_ci	}
1258c2ecf20Sopenharmony_ci	fd = open(av[1], O_RDWR);
1268c2ecf20Sopenharmony_ci	if (fd < 0) {
1278c2ecf20Sopenharmony_ci		perror(av[1]);
1288c2ecf20Sopenharmony_ci		exit(1);
1298c2ecf20Sopenharmony_ci	}
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci	nnote = 12 + ROUNDUP(strlen(arch) + 1) + sizeof(descr);
1328c2ecf20Sopenharmony_ci	nnote2 = 12 + ROUNDUP(strlen(rpaname) + 1) + sizeof(rpanote);
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	n = read(fd, buf, sizeof(buf));
1358c2ecf20Sopenharmony_ci	if (n < 0) {
1368c2ecf20Sopenharmony_ci		perror("read");
1378c2ecf20Sopenharmony_ci		exit(1);
1388c2ecf20Sopenharmony_ci	}
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci	if (memcmp(&buf[E_IDENT+EI_MAGIC], elf_magic, 4) != 0)
1418c2ecf20Sopenharmony_ci		goto notelf;
1428c2ecf20Sopenharmony_ci	e_class = buf[E_IDENT+EI_CLASS];
1438c2ecf20Sopenharmony_ci	if (e_class != ELFCLASS32 && e_class != ELFCLASS64)
1448c2ecf20Sopenharmony_ci		goto notelf;
1458c2ecf20Sopenharmony_ci	e_data = buf[E_IDENT+EI_DATA];
1468c2ecf20Sopenharmony_ci	if (e_data != ELFDATA2MSB && e_data != ELFDATA2LSB)
1478c2ecf20Sopenharmony_ci		goto notelf;
1488c2ecf20Sopenharmony_ci	if (n < E_HSIZE)
1498c2ecf20Sopenharmony_ci		goto notelf;
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci	ph = (e_class == ELFCLASS32 ? GET_32(E_PHOFF) : GET_64(E_PHOFF));
1528c2ecf20Sopenharmony_ci	ps = GET_16(E_PHENTSIZE);
1538c2ecf20Sopenharmony_ci	np = GET_16(E_PHNUM);
1548c2ecf20Sopenharmony_ci	if (ph < E_HSIZE || ps < PH_HSIZE || np < 1)
1558c2ecf20Sopenharmony_ci		goto notelf;
1568c2ecf20Sopenharmony_ci	if (ph + (np + 2) * ps + nnote + nnote2 > n)
1578c2ecf20Sopenharmony_ci		goto nospace;
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	for (i = 0; i < np; ++i) {
1608c2ecf20Sopenharmony_ci		if (GET_32(ph + PH_TYPE) == PT_NOTE) {
1618c2ecf20Sopenharmony_ci			fprintf(stderr, "%s already has a note entry\n",
1628c2ecf20Sopenharmony_ci				av[1]);
1638c2ecf20Sopenharmony_ci			exit(0);
1648c2ecf20Sopenharmony_ci		}
1658c2ecf20Sopenharmony_ci		ph += ps;
1668c2ecf20Sopenharmony_ci	}
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci	/* XXX check that the area we want to use is all zeroes */
1698c2ecf20Sopenharmony_ci	for (i = 0; i < 2 * ps + nnote + nnote2; ++i)
1708c2ecf20Sopenharmony_ci		if (buf[ph + i] != 0)
1718c2ecf20Sopenharmony_ci			goto nospace;
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci	/* fill in the program header entry */
1748c2ecf20Sopenharmony_ci	ns = ph + 2 * ps;
1758c2ecf20Sopenharmony_ci	PUT_32(ph + PH_TYPE, PT_NOTE);
1768c2ecf20Sopenharmony_ci	if (e_class == ELFCLASS32)
1778c2ecf20Sopenharmony_ci		PUT_32(ph + PH_OFFSET, ns);
1788c2ecf20Sopenharmony_ci	else
1798c2ecf20Sopenharmony_ci		PUT_64(ph + PH_OFFSET, ns);
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci	if (e_class == ELFCLASS32)
1828c2ecf20Sopenharmony_ci		PUT_32(ph + PH_FILESZ, nnote);
1838c2ecf20Sopenharmony_ci	else
1848c2ecf20Sopenharmony_ci		PUT_64(ph + PH_FILESZ, nnote);
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci	/* fill in the note area we point to */
1878c2ecf20Sopenharmony_ci	/* XXX we should probably make this a proper section */
1888c2ecf20Sopenharmony_ci	PUT_32(ns, strlen(arch) + 1);
1898c2ecf20Sopenharmony_ci	PUT_32(ns + 4, N_DESCR * 4);
1908c2ecf20Sopenharmony_ci	PUT_32(ns + 8, 0x1275);
1918c2ecf20Sopenharmony_ci	strcpy((char *) &buf[ns + 12], arch);
1928c2ecf20Sopenharmony_ci	ns += 12 + strlen(arch) + 1;
1938c2ecf20Sopenharmony_ci	for (i = 0; i < N_DESCR; ++i, ns += 4)
1948c2ecf20Sopenharmony_ci		PUT_32BE(ns, descr[i]);
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci	/* fill in the second program header entry and the RPA note area */
1978c2ecf20Sopenharmony_ci	ph += ps;
1988c2ecf20Sopenharmony_ci	PUT_32(ph + PH_TYPE, PT_NOTE);
1998c2ecf20Sopenharmony_ci	if (e_class == ELFCLASS32)
2008c2ecf20Sopenharmony_ci		PUT_32(ph + PH_OFFSET, ns);
2018c2ecf20Sopenharmony_ci	else
2028c2ecf20Sopenharmony_ci		PUT_64(ph + PH_OFFSET, ns);
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci	if (e_class == ELFCLASS32)
2058c2ecf20Sopenharmony_ci		PUT_32(ph + PH_FILESZ, nnote);
2068c2ecf20Sopenharmony_ci	else
2078c2ecf20Sopenharmony_ci		PUT_64(ph + PH_FILESZ, nnote2);
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci	/* fill in the note area we point to */
2108c2ecf20Sopenharmony_ci	PUT_32(ns, strlen(rpaname) + 1);
2118c2ecf20Sopenharmony_ci	PUT_32(ns + 4, sizeof(rpanote));
2128c2ecf20Sopenharmony_ci	PUT_32(ns + 8, 0x12759999);
2138c2ecf20Sopenharmony_ci	strcpy((char *) &buf[ns + 12], rpaname);
2148c2ecf20Sopenharmony_ci	ns += 12 + ROUNDUP(strlen(rpaname) + 1);
2158c2ecf20Sopenharmony_ci	for (i = 0; i < N_RPA_DESCR; ++i, ns += 4)
2168c2ecf20Sopenharmony_ci		PUT_32BE(ns, rpanote[i]);
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci	/* Update the number of program headers */
2198c2ecf20Sopenharmony_ci	PUT_16(E_PHNUM, np + 2);
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci	/* write back */
2228c2ecf20Sopenharmony_ci	i = lseek(fd, (long) 0, SEEK_SET);
2238c2ecf20Sopenharmony_ci	if (i < 0) {
2248c2ecf20Sopenharmony_ci		perror("lseek");
2258c2ecf20Sopenharmony_ci		exit(1);
2268c2ecf20Sopenharmony_ci	}
2278c2ecf20Sopenharmony_ci	i = write(fd, buf, n);
2288c2ecf20Sopenharmony_ci	if (i < 0) {
2298c2ecf20Sopenharmony_ci		perror("write");
2308c2ecf20Sopenharmony_ci		exit(1);
2318c2ecf20Sopenharmony_ci	}
2328c2ecf20Sopenharmony_ci	if (i < n) {
2338c2ecf20Sopenharmony_ci		fprintf(stderr, "%s: write truncated\n", av[1]);
2348c2ecf20Sopenharmony_ci		exit(1);
2358c2ecf20Sopenharmony_ci	}
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci	exit(0);
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci notelf:
2408c2ecf20Sopenharmony_ci	fprintf(stderr, "%s does not appear to be an ELF file\n", av[1]);
2418c2ecf20Sopenharmony_ci	exit(1);
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci nospace:
2448c2ecf20Sopenharmony_ci	fprintf(stderr, "sorry, I can't find space in %s to put the note\n",
2458c2ecf20Sopenharmony_ci		av[1]);
2468c2ecf20Sopenharmony_ci	exit(1);
2478c2ecf20Sopenharmony_ci}
248