xref: /freebsd/stand/powerpc/ofw/cas.c (revision bf2fa8d9d11c9f2ceff09bacc406876fa37096be)
1 /*-
2  * Copyright (c) 2019 Leandro Lupori
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
14  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23  * SUCH DAMAGE.
24  */
25 
26 #include <sys/cdefs.h>
27 __FBSDID("$FreeBSD$");
28 
29 #include <openfirm.h>
30 #include <stand.h>
31 
32 #include <sys/endian.h>
33 
34 /* #define CAS_DEBUG */
35 #ifdef CAS_DEBUG
36 #define DPRINTF(fmt, ...)	printf(fmt, ## __VA_ARGS__)
37 #else
38 #define DPRINTF(fmt, ...)	do { ; } while (0)
39 #endif
40 
41 /* PVR */
42 #define PVR_CPU_P8E		0x004b0000
43 #define PVR_CPU_P8NVL		0x004c0000
44 #define PVR_CPU_P8		0x004d0000
45 #define PVR_CPU_P9		0x004e0000
46 #define PVR_CPU_MASK		0xffff0000
47 
48 #define PVR_ISA_207		0x0f000004
49 #define PVR_ISA_300		0x0f000005
50 #define PVR_ISA_MASK		0xffffffff
51 
52 /* loader version of kernel's CPU_MAXSIZE */
53 #define MAX_CPUS		((uint32_t)256u)
54 
55 /* Option Vectors' settings */
56 
57 /* length of ignored OV */
58 #define OV_IGN_LEN		0
59 
60 /* byte 1 (of any OV) */
61 #define OV_IGN			0x80
62 
63 /* Option Vector 5 */
64 
65 /* byte 2 */
66 #define OV5_LPAR		0x80
67 #define OV5_SPLPAR		0x40
68 #define OV5_DRMEM		0x20
69 #define OV5_LP			0x10
70 #define OV5_ALPHA_PART		0x08
71 #define OV5_DMA_DELAY		0x04
72 #define OV5_DONATE_CPU		0x02
73 #define OV5_MSI			0x01
74 
75 /* 9-12: max cpus */
76 #define OV5_MAX_CPUS(n)		((MAX_CPUS >> (3*8 - (n)*8)) & 0xff)
77 
78 /* 13-14: LoPAPR Level */
79 #define LOPAPR_LEVEL		0x0101	/* 1.1 */
80 #define OV5_LOPAPR_LEVEL(n)	((LOPAPR_LEVEL >> (8 - (n)*8)) & 0xff)
81 
82 /* byte 17: Platform Facilities */
83 #define OV5_RNG			0x80
84 #define OV5_COMP_ENG		0x40
85 #define OV5_ENC_ENG		0x20
86 
87 /* byte 21: Sub-Processors */
88 #define OV5_NO_SUBPROCS		0
89 #define OV5_SUBPROCS		1
90 
91 /* byte 23: interrupt controller */
92 #define OV5_INTC_XICS		0
93 
94 /* byte 24: MMU */
95 #define OV5_MMU_INDEX		24
96 #define OV5_MMU_HPT		0
97 #define OV5_MMU_RADIX		0x40
98 #define OV5_MMU_EITHER		0x80
99 #define OV5_MMU_DYNAMIC		0xc0
100 
101 /* byte 25: HPT MMU Extensions */
102 #define OV5_HPT_EXT_INDEX	25
103 #define OV5_HPT_GTSE		0x40
104 
105 /* byte 26: Radix MMU Extensions */
106 #define OV5_RADIX_EXT_INDEX	26
107 #define OV5_RADIX_GTSE		0x40
108 
109 
110 struct pvr {
111 	uint32_t	mask;
112 	uint32_t	val;
113 };
114 
115 struct opt_vec_ignore {
116 	char	data[2];
117 } __packed;
118 
119 struct opt_vec4 {
120 	char data[3];
121 } __packed;
122 
123 struct opt_vec5 {
124 	char data[27];
125 } __packed;
126 
127 static struct ibm_arch_vec {
128 	struct pvr		pvr_list[7];
129 	uint8_t			num_opts;
130 	struct opt_vec_ignore	vec1;
131 	struct opt_vec_ignore	vec2;
132 	struct opt_vec_ignore	vec3;
133 	struct opt_vec4		vec4;
134 	struct opt_vec5		vec5;
135 } __packed ibm_arch_vec = {
136 	/* pvr_list */ {
137 		{ htobe32(PVR_CPU_MASK), htobe32(PVR_CPU_P8) },
138 		{ htobe32(PVR_CPU_MASK), htobe32(PVR_CPU_P8E) },
139 		{ htobe32(PVR_CPU_MASK), htobe32(PVR_CPU_P8NVL) },
140 		{ htobe32(PVR_CPU_MASK), htobe32(PVR_CPU_P9) },
141 		{ htobe32(PVR_ISA_MASK), htobe32(PVR_ISA_207) },
142 		{ htobe32(PVR_ISA_MASK), htobe32(PVR_ISA_300) },
143 		{ 0, 0xffffffffu }			/* terminator */
144 	},
145 	4,	/* num_opts (4 actually means 5 option vectors) */
146 	{ OV_IGN_LEN, OV_IGN },		/* OV1 */
147 	{ OV_IGN_LEN, OV_IGN },		/* OV2 */
148 	{ OV_IGN_LEN, OV_IGN },		/* OV3 */
149 	/* OV4 (can't be ignored) */ {
150 		sizeof(struct opt_vec4) - 2,	/* length (n-2) */
151 		0,
152 		10 /* Minimum VP entitled capacity percentage * 100
153 		    * (if absent assume 10%) */
154 	},
155 	/* OV5 */ {
156 		sizeof(struct opt_vec5) - 2,	/* length (n-2) */
157 		0,				/* don't ignore */
158 		OV5_LPAR | OV5_SPLPAR | OV5_LP | OV5_MSI,
159 		0,
160 		0,	/* Cooperative Memory Over-commitment */
161 		0,	/* Associativity Information Option */
162 		0,	/* Binary Option Controls */
163 		0,	/* Reserved */
164 		0,	/* Reserved */
165 		OV5_MAX_CPUS(0),
166 		OV5_MAX_CPUS(1),		/* 10 */
167 		OV5_MAX_CPUS(2),
168 		OV5_MAX_CPUS(3),
169 		OV5_LOPAPR_LEVEL(0),
170 		OV5_LOPAPR_LEVEL(1),
171 		0,	/* Reserved */
172 		0,	/* Reserved */
173 		0,	/* Platform Facilities */
174 		0,	/* Reserved */
175 		0,	/* Reserved */
176 		0,	/* Reserved */		/* 20 */
177 		OV5_NO_SUBPROCS,
178 		0,	/* DRMEM_V2 */
179 		OV5_INTC_XICS,
180 		OV5_MMU_HPT,
181 		0,
182 		0
183 	}
184 };
185 
186 int
187 ppc64_cas(void)
188 {
189 	phandle_t pkg;
190 	ihandle_t inst;
191 	cell_t err;
192 	uint8_t buf[16], idx, val;
193 	int i, len, rc, radix_mmu;
194 	const char *var;
195 	char *ov5;
196 
197 	pkg = OF_finddevice("/chosen");
198 	if (pkg == -1) {
199 		printf("cas: couldn't find /chosen\n");
200 		return (-1);
201 	}
202 
203 	len = OF_getprop(pkg, "ibm,arch-vec-5-platform-support", buf,
204 	    sizeof(buf));
205 	if (len == -1)
206 		/* CAS not supported */
207 		return (0);
208 
209 	radix_mmu = 0;
210 	ov5 = ibm_arch_vec.vec5.data;
211 	for (i = 0; i < len; i += 2) {
212 		idx = buf[i];
213 		val = buf[i + 1];
214 		DPRINTF("idx 0x%02x val 0x%02x\n", idx, val);
215 
216 		switch (idx) {
217 		case OV5_MMU_INDEX:
218 			/*
219 			 * Note that testing for OV5_MMU_RADIX/OV5_MMU_EITHER
220 			 * also covers OV5_MMU_DYNAMIC.
221 			 */
222 			if ((val & OV5_MMU_RADIX) || (val & OV5_MMU_EITHER))
223 				radix_mmu = 1;
224 			break;
225 
226 		case OV5_RADIX_EXT_INDEX:
227 			if (val & OV5_RADIX_GTSE)
228 				ov5[idx] = OV5_RADIX_GTSE;
229 			break;
230 
231 		case OV5_HPT_EXT_INDEX:
232 		default:
233 			break;
234 		}
235 	}
236 
237 	if (!radix_mmu)
238 		/*
239 		 * If radix is not supported, set radix_mmu to 0 to avoid
240 		 * the kernel trying to use it and panic.
241 		 */
242 		setenv("radix_mmu", "0", 1);
243 	else if ((var = getenv("radix_mmu")) != NULL && var[0] == '0')
244 		radix_mmu = 0;
245 	else
246 		ov5[OV5_MMU_INDEX] = OV5_MMU_RADIX;
247 
248 	inst = OF_open("/");
249 	if (inst == -1) {
250 		printf("cas: failed to open / node\n");
251 		return (-1);
252 	}
253 
254 	DPRINTF("MMU 0x%02x RADIX_EXT 0x%02x\n",
255 	    ov5[OV5_MMU_INDEX], ov5[OV5_RADIX_EXT_INDEX]);
256 	rc = OF_call_method("ibm,client-architecture-support",
257 	    inst, 1, 1, &ibm_arch_vec, &err);
258 	if (rc != 0 || err) {
259 		printf("cas: CAS method returned an error: rc %d err %jd\n",
260 		    rc, (intmax_t)err);
261 		rc = -1;
262 	}
263 
264 	OF_close(inst);
265 	printf("cas: selected %s MMU\n", radix_mmu ? "radix" : "hash");
266 	return (rc);
267 }
268