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 <openfirm.h>
27 #include <stand.h>
28
29 #include <sys/endian.h>
30
31 /* #define CAS_DEBUG */
32 #ifdef CAS_DEBUG
33 #define DPRINTF(fmt, ...) printf(fmt, ## __VA_ARGS__)
34 #else
35 #define DPRINTF(fmt, ...) do { ; } while (0)
36 #endif
37
38 /* PVR */
39 #define PVR_CPU_P6 0x003e0000
40 #define PVR_CPU_P7 0x003f0000
41 #define PVR_CPU_P7PLUS 0x004a0000
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_P10 0x00800000
47 #define PVR_CPU_P11 0x00820000
48 #define PVR_CPU_MASK 0xffff0000
49
50 #define PVR_ISA_207 0x0f000004
51 #define PVR_ISA_300 0x0f000005
52 #define PVR_ISA_31 0x0f000006
53 #define PVR_ISA_MASK 0xffffffff
54
55 /* loader version of kernel's CPU_MAXSIZE */
56 #define MAX_CPUS ((uint32_t)256u)
57
58 /* Option Vectors' settings */
59
60 /* length of ignored OV */
61 #define OV_IGN_LEN 0
62
63 /* byte 1 (of any OV) */
64 #define OV_IGN 0x80
65
66 /* Option Vector 5 */
67
68 /* byte 2 */
69 #define OV5_LPAR 0x80
70 #define OV5_SPLPAR 0x40
71 #define OV5_DRMEM 0x20
72 #define OV5_LP 0x10
73 #define OV5_ALPHA_PART 0x08
74 #define OV5_DMA_DELAY 0x04
75 #define OV5_DONATE_CPU 0x02
76 #define OV5_MSI 0x01
77
78 /* 9-12: max cpus */
79 #define OV5_MAX_CPUS(n) ((MAX_CPUS >> (3*8 - (n)*8)) & 0xff)
80
81 /* 13-14: LoPAPR Level */
82 #define LOPAPR_LEVEL 0x0101 /* 1.1 */
83 #define OV5_LOPAPR_LEVEL(n) ((LOPAPR_LEVEL >> (8 - (n)*8)) & 0xff)
84
85 /* byte 17: Platform Facilities */
86 #define OV5_RNG 0x80
87 #define OV5_COMP_ENG 0x40
88 #define OV5_ENC_ENG 0x20
89
90 /* byte 21: Sub-Processors */
91 #define OV5_NO_SUBPROCS 0
92 #define OV5_SUBPROCS 1
93
94 /* byte 23: interrupt controller */
95 #define OV5_INTC_XICS 0
96
97 /* byte 24: MMU */
98 #define OV5_MMU_INDEX 24
99 #define OV5_MMU_HPT 0
100 #define OV5_MMU_RADIX 0x40
101 #define OV5_MMU_EITHER 0x80
102 #define OV5_MMU_DYNAMIC 0xc0
103
104 /* byte 25: HPT MMU Extensions */
105 #define OV5_HPT_EXT_INDEX 25
106 #define OV5_HPT_GTSE 0x40
107
108 /* byte 26: Radix MMU Extensions */
109 #define OV5_RADIX_EXT_INDEX 26
110 #define OV5_RADIX_GTSE 0x40
111
112
113 struct pvr {
114 uint32_t mask;
115 uint32_t val;
116 };
117
118 struct opt_vec_ignore {
119 char data[2];
120 } __packed;
121
122 struct opt_vec4 {
123 char data[3];
124 } __packed;
125
126 struct opt_vec5 {
127 char data[27];
128 } __packed;
129
130 static struct ibm_arch_vec {
131 struct pvr pvr_list[13];
132 uint8_t num_opts;
133 struct opt_vec_ignore vec1;
134 struct opt_vec_ignore vec2;
135 struct opt_vec_ignore vec3;
136 struct opt_vec4 vec4;
137 struct opt_vec5 vec5;
138 } __packed ibm_arch_vec = {
139 /* pvr_list */ {
140 { htobe32(PVR_CPU_MASK), htobe32(PVR_CPU_P6) },
141 { htobe32(PVR_CPU_MASK), htobe32(PVR_CPU_P7) },
142 { htobe32(PVR_CPU_MASK), htobe32(PVR_CPU_P7PLUS) },
143 { htobe32(PVR_CPU_MASK), htobe32(PVR_CPU_P8) },
144 { htobe32(PVR_CPU_MASK), htobe32(PVR_CPU_P8E) },
145 { htobe32(PVR_CPU_MASK), htobe32(PVR_CPU_P8NVL) },
146 { htobe32(PVR_CPU_MASK), htobe32(PVR_CPU_P9) },
147 { htobe32(PVR_CPU_MASK), htobe32(PVR_CPU_P10) },
148 { htobe32(PVR_CPU_MASK), htobe32(PVR_CPU_P11) },
149 { htobe32(PVR_ISA_MASK), htobe32(PVR_ISA_207) },
150 { htobe32(PVR_ISA_MASK), htobe32(PVR_ISA_300) },
151 { htobe32(PVR_ISA_MASK), htobe32(PVR_ISA_31) },
152 { 0, 0xffffffffu } /* terminator */
153 },
154 4, /* num_opts (4 actually means 5 option vectors) */
155 { OV_IGN_LEN, OV_IGN }, /* OV1 */
156 { OV_IGN_LEN, OV_IGN }, /* OV2 */
157 { OV_IGN_LEN, OV_IGN }, /* OV3 */
158 /* OV4 (can't be ignored) */ {
159 sizeof(struct opt_vec4) - 2, /* length (n-2) */
160 0,
161 10 /* Minimum VP entitled capacity percentage * 100
162 * (if absent assume 10%) */
163 },
164 /* OV5 */ {
165 sizeof(struct opt_vec5) - 2, /* length (n-2) */
166 0, /* don't ignore */
167 OV5_LPAR | OV5_SPLPAR | OV5_LP | OV5_MSI,
168 0,
169 0, /* Cooperative Memory Over-commitment */
170 0, /* Associativity Information Option */
171 0, /* Binary Option Controls */
172 0, /* Reserved */
173 0, /* Reserved */
174 OV5_MAX_CPUS(0),
175 OV5_MAX_CPUS(1), /* 10 */
176 OV5_MAX_CPUS(2),
177 OV5_MAX_CPUS(3),
178 OV5_LOPAPR_LEVEL(0),
179 OV5_LOPAPR_LEVEL(1),
180 0, /* Reserved */
181 0, /* Reserved */
182 0, /* Platform Facilities */
183 0, /* Reserved */
184 0, /* Reserved */
185 0, /* Reserved */ /* 20 */
186 OV5_NO_SUBPROCS,
187 0, /* DRMEM_V2 */
188 OV5_INTC_XICS,
189 OV5_MMU_HPT,
190 0,
191 0
192 }
193 };
194
195 int
ppc64_cas(void)196 ppc64_cas(void)
197 {
198 phandle_t pkg;
199 ihandle_t inst;
200 cell_t err = 0;
201 uint8_t buf[16], idx, val;
202 int i, len, rc, radix_mmu;
203 const char *var;
204 char *ov5;
205
206 pkg = OF_finddevice("/chosen");
207 if (pkg == -1) {
208 printf("cas: couldn't find /chosen\n");
209 return (-1);
210 }
211
212 len = OF_getprop(pkg, "ibm,arch-vec-5-platform-support", buf,
213 sizeof(buf));
214 if (len == -1)
215 /* CAS not supported */
216 return (0);
217
218 radix_mmu = 0;
219 ov5 = ibm_arch_vec.vec5.data;
220 for (i = 0; i < len; i += 2) {
221 idx = buf[i];
222 val = buf[i + 1];
223 DPRINTF("idx 0x%02x val 0x%02x\n", idx, val);
224
225 switch (idx) {
226 case OV5_MMU_INDEX:
227 /*
228 * Note that testing for OV5_MMU_RADIX/OV5_MMU_EITHER
229 * also covers OV5_MMU_DYNAMIC.
230 */
231 if ((val & OV5_MMU_RADIX) || (val & OV5_MMU_EITHER))
232 radix_mmu = 1;
233 break;
234
235 case OV5_RADIX_EXT_INDEX:
236 if (val & OV5_RADIX_GTSE)
237 ov5[idx] = OV5_RADIX_GTSE;
238 break;
239
240 case OV5_HPT_EXT_INDEX:
241 default:
242 break;
243 }
244 }
245
246 if (!radix_mmu)
247 /*
248 * If radix is not supported, set radix_mmu to 0 to avoid
249 * the kernel trying to use it and panic.
250 */
251 setenv("radix_mmu", "0", 1);
252 else if ((var = getenv("radix_mmu")) != NULL && var[0] == '0')
253 radix_mmu = 0;
254 else
255 ov5[OV5_MMU_INDEX] = OV5_MMU_RADIX;
256
257 inst = OF_open("/");
258 if (inst == -1) {
259 printf("cas: failed to open / node\n");
260 return (-1);
261 }
262
263 DPRINTF("MMU 0x%02x RADIX_EXT 0x%02x\n",
264 ov5[OV5_MMU_INDEX], ov5[OV5_RADIX_EXT_INDEX]);
265 rc = OF_call_method("ibm,client-architecture-support",
266 inst, 1, 1, &ibm_arch_vec, &err);
267 if (rc != 0 || err) {
268 printf("cas: CAS method returned an error: rc %d err %jd\n",
269 rc, (intmax_t)err);
270 rc = -1;
271 }
272
273 OF_close(inst);
274 printf("cas: selected %s MMU\n", radix_mmu ? "radix" : "hash");
275 return (rc);
276 }
277