xref: /kernel/linux/linux-6.6/arch/powerpc/boot/oflib.c (revision 62306a36)
162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) Paul Mackerras 1997.
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci#include <stddef.h>
662306a36Sopenharmony_ci#include "types.h"
762306a36Sopenharmony_ci#include "elf.h"
862306a36Sopenharmony_ci#include "string.h"
962306a36Sopenharmony_ci#include "stdio.h"
1062306a36Sopenharmony_ci#include "page.h"
1162306a36Sopenharmony_ci#include "ops.h"
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#include "of.h"
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_citypedef u32 prom_arg_t;
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci/* The following structure is used to communicate with open firmware.
1862306a36Sopenharmony_ci * All arguments in and out are in big endian format. */
1962306a36Sopenharmony_cistruct prom_args {
2062306a36Sopenharmony_ci	__be32 service;	/* Address of service name string. */
2162306a36Sopenharmony_ci	__be32 nargs;	/* Number of input arguments. */
2262306a36Sopenharmony_ci	__be32 nret;	/* Number of output arguments. */
2362306a36Sopenharmony_ci	__be32 args[10];	/* Input/output arguments. */
2462306a36Sopenharmony_ci};
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci#ifdef __powerpc64__
2762306a36Sopenharmony_ciextern int prom(void *);
2862306a36Sopenharmony_ci#else
2962306a36Sopenharmony_cistatic int (*prom) (void *);
3062306a36Sopenharmony_ci#endif
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_civoid of_init(void *promptr)
3362306a36Sopenharmony_ci{
3462306a36Sopenharmony_ci#ifndef __powerpc64__
3562306a36Sopenharmony_ci	prom = (int (*)(void *))promptr;
3662306a36Sopenharmony_ci#endif
3762306a36Sopenharmony_ci}
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci#define ADDR(x)		(u32)(unsigned long)(x)
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ciint of_call_prom(const char *service, int nargs, int nret, ...)
4262306a36Sopenharmony_ci{
4362306a36Sopenharmony_ci	int i;
4462306a36Sopenharmony_ci	struct prom_args args;
4562306a36Sopenharmony_ci	va_list list;
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci	args.service = cpu_to_be32(ADDR(service));
4862306a36Sopenharmony_ci	args.nargs = cpu_to_be32(nargs);
4962306a36Sopenharmony_ci	args.nret = cpu_to_be32(nret);
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci	va_start(list, nret);
5262306a36Sopenharmony_ci	for (i = 0; i < nargs; i++)
5362306a36Sopenharmony_ci		args.args[i] = cpu_to_be32(va_arg(list, prom_arg_t));
5462306a36Sopenharmony_ci	va_end(list);
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	for (i = 0; i < nret; i++)
5762306a36Sopenharmony_ci		args.args[nargs+i] = 0;
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	if (prom(&args) < 0)
6062306a36Sopenharmony_ci		return PROM_ERROR;
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	return (nret > 0) ? be32_to_cpu(args.args[nargs]) : 0;
6362306a36Sopenharmony_ci}
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_cistatic int of_call_prom_ret(const char *service, int nargs, int nret,
6662306a36Sopenharmony_ci			    prom_arg_t *rets, ...)
6762306a36Sopenharmony_ci{
6862306a36Sopenharmony_ci	int i;
6962306a36Sopenharmony_ci	struct prom_args args;
7062306a36Sopenharmony_ci	va_list list;
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	args.service = cpu_to_be32(ADDR(service));
7362306a36Sopenharmony_ci	args.nargs = cpu_to_be32(nargs);
7462306a36Sopenharmony_ci	args.nret = cpu_to_be32(nret);
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	va_start(list, rets);
7762306a36Sopenharmony_ci	for (i = 0; i < nargs; i++)
7862306a36Sopenharmony_ci		args.args[i] = cpu_to_be32(va_arg(list, prom_arg_t));
7962306a36Sopenharmony_ci	va_end(list);
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	for (i = 0; i < nret; i++)
8262306a36Sopenharmony_ci		args.args[nargs+i] = 0;
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	if (prom(&args) < 0)
8562306a36Sopenharmony_ci		return PROM_ERROR;
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	if (rets != NULL)
8862306a36Sopenharmony_ci		for (i = 1; i < nret; ++i)
8962306a36Sopenharmony_ci			rets[i-1] = be32_to_cpu(args.args[nargs+i]);
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	return (nret > 0) ? be32_to_cpu(args.args[nargs]) : 0;
9262306a36Sopenharmony_ci}
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci/* returns true if s2 is a prefix of s1 */
9562306a36Sopenharmony_cistatic int string_match(const char *s1, const char *s2)
9662306a36Sopenharmony_ci{
9762306a36Sopenharmony_ci	for (; *s2; ++s2)
9862306a36Sopenharmony_ci		if (*s1++ != *s2)
9962306a36Sopenharmony_ci			return 0;
10062306a36Sopenharmony_ci	return 1;
10162306a36Sopenharmony_ci}
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci/*
10462306a36Sopenharmony_ci * Older OF's require that when claiming a specific range of addresses,
10562306a36Sopenharmony_ci * we claim the physical space in the /memory node and the virtual
10662306a36Sopenharmony_ci * space in the chosen mmu node, and then do a map operation to
10762306a36Sopenharmony_ci * map virtual to physical.
10862306a36Sopenharmony_ci */
10962306a36Sopenharmony_cistatic int need_map = -1;
11062306a36Sopenharmony_cistatic ihandle chosen_mmu;
11162306a36Sopenharmony_cistatic ihandle memory;
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_cistatic int check_of_version(void)
11462306a36Sopenharmony_ci{
11562306a36Sopenharmony_ci	phandle oprom, chosen;
11662306a36Sopenharmony_ci	char version[64];
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	oprom = of_finddevice("/openprom");
11962306a36Sopenharmony_ci	if (oprom == (phandle) -1)
12062306a36Sopenharmony_ci		return 0;
12162306a36Sopenharmony_ci	if (of_getprop(oprom, "model", version, sizeof(version)) <= 0)
12262306a36Sopenharmony_ci		return 0;
12362306a36Sopenharmony_ci	version[sizeof(version)-1] = 0;
12462306a36Sopenharmony_ci	printf("OF version = '%s'\r\n", version);
12562306a36Sopenharmony_ci	if (!string_match(version, "Open Firmware, 1.")
12662306a36Sopenharmony_ci	    && !string_match(version, "FirmWorks,3."))
12762306a36Sopenharmony_ci		return 0;
12862306a36Sopenharmony_ci	chosen = of_finddevice("/chosen");
12962306a36Sopenharmony_ci	if (chosen == (phandle) -1) {
13062306a36Sopenharmony_ci		chosen = of_finddevice("/chosen@0");
13162306a36Sopenharmony_ci		if (chosen == (phandle) -1) {
13262306a36Sopenharmony_ci			printf("no chosen\n");
13362306a36Sopenharmony_ci			return 0;
13462306a36Sopenharmony_ci		}
13562306a36Sopenharmony_ci	}
13662306a36Sopenharmony_ci	if (of_getprop(chosen, "mmu", &chosen_mmu, sizeof(chosen_mmu)) <= 0) {
13762306a36Sopenharmony_ci		printf("no mmu\n");
13862306a36Sopenharmony_ci		return 0;
13962306a36Sopenharmony_ci	}
14062306a36Sopenharmony_ci	memory = of_call_prom("open", 1, 1, "/memory");
14162306a36Sopenharmony_ci	if (memory == PROM_ERROR) {
14262306a36Sopenharmony_ci		memory = of_call_prom("open", 1, 1, "/memory@0");
14362306a36Sopenharmony_ci		if (memory == PROM_ERROR) {
14462306a36Sopenharmony_ci			printf("no memory node\n");
14562306a36Sopenharmony_ci			return 0;
14662306a36Sopenharmony_ci		}
14762306a36Sopenharmony_ci	}
14862306a36Sopenharmony_ci	printf("old OF detected\r\n");
14962306a36Sopenharmony_ci	return 1;
15062306a36Sopenharmony_ci}
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ciunsigned int of_claim(unsigned long virt, unsigned long size,
15362306a36Sopenharmony_ci		      unsigned long align)
15462306a36Sopenharmony_ci{
15562306a36Sopenharmony_ci	int ret;
15662306a36Sopenharmony_ci	prom_arg_t result;
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	if (need_map < 0)
15962306a36Sopenharmony_ci		need_map = check_of_version();
16062306a36Sopenharmony_ci	if (align || !need_map)
16162306a36Sopenharmony_ci		return of_call_prom("claim", 3, 1, virt, size, align);
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	ret = of_call_prom_ret("call-method", 5, 2, &result, "claim", memory,
16462306a36Sopenharmony_ci			       align, size, virt);
16562306a36Sopenharmony_ci	if (ret != 0 || result == -1)
16662306a36Sopenharmony_ci		return  -1;
16762306a36Sopenharmony_ci	ret = of_call_prom_ret("call-method", 5, 2, &result, "claim", chosen_mmu,
16862306a36Sopenharmony_ci			       align, size, virt);
16962306a36Sopenharmony_ci	/* 0x12 == coherent + read/write */
17062306a36Sopenharmony_ci	ret = of_call_prom("call-method", 6, 1, "map", chosen_mmu,
17162306a36Sopenharmony_ci			   0x12, size, virt, virt);
17262306a36Sopenharmony_ci	return virt;
17362306a36Sopenharmony_ci}
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_civoid *of_vmlinux_alloc(unsigned long size)
17662306a36Sopenharmony_ci{
17762306a36Sopenharmony_ci	unsigned long start = (unsigned long)_start, end = (unsigned long)_end;
17862306a36Sopenharmony_ci	unsigned long addr;
17962306a36Sopenharmony_ci	void *p;
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci	/* With some older POWER4 firmware we need to claim the area the kernel
18262306a36Sopenharmony_ci	 * will reside in.  Newer firmwares don't need this so we just ignore
18362306a36Sopenharmony_ci	 * the return value.
18462306a36Sopenharmony_ci	 */
18562306a36Sopenharmony_ci	addr = (unsigned long) of_claim(start, end - start, 0);
18662306a36Sopenharmony_ci	printf("Trying to claim from 0x%lx to 0x%lx (0x%lx) got %lx\r\n",
18762306a36Sopenharmony_ci	       start, end, end - start, addr);
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	p = malloc(size);
19062306a36Sopenharmony_ci	if (!p)
19162306a36Sopenharmony_ci		fatal("Can't allocate memory for kernel image!\n\r");
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	return p;
19462306a36Sopenharmony_ci}
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_civoid of_exit(void)
19762306a36Sopenharmony_ci{
19862306a36Sopenharmony_ci	of_call_prom("exit", 0, 0);
19962306a36Sopenharmony_ci}
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci/*
20262306a36Sopenharmony_ci * OF device tree routines
20362306a36Sopenharmony_ci */
20462306a36Sopenharmony_civoid *of_finddevice(const char *name)
20562306a36Sopenharmony_ci{
20662306a36Sopenharmony_ci	return (void *) (unsigned long) of_call_prom("finddevice", 1, 1, name);
20762306a36Sopenharmony_ci}
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ciint of_getprop(const void *phandle, const char *name, void *buf,
21062306a36Sopenharmony_ci	       const int buflen)
21162306a36Sopenharmony_ci{
21262306a36Sopenharmony_ci	return of_call_prom("getprop", 4, 1, phandle, name, buf, buflen);
21362306a36Sopenharmony_ci}
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ciint of_setprop(const void *phandle, const char *name, const void *buf,
21662306a36Sopenharmony_ci	       const int buflen)
21762306a36Sopenharmony_ci{
21862306a36Sopenharmony_ci	return of_call_prom("setprop", 4, 1, phandle, name, buf, buflen);
21962306a36Sopenharmony_ci}
220