xref: /freebsd/sys/riscv/riscv/identcpu.c (revision 4d2f90f5a694b89240c494c01a5c07e24131f6f0)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2015-2026 Ruslan Bukin <br@bsdpad.com>
5  * All rights reserved.
6  * Copyright (c) 2022 Mitchell Horne <mhorne@FreeBSD.org>
7  * Copyright (c) 2023 The FreeBSD Foundation
8  *
9  * Portions of this software were developed by SRI International and the
10  * University of Cambridge Computer Laboratory under DARPA/AFRL contract
11  * FA8750-10-C-0237 ("CTSRD"), as part of the DARPA CRASH research programme.
12  *
13  * Portions of this software were developed by the University of Cambridge
14  * Computer Laboratory as part of the CTSRD Project, with support from the
15  * UK Higher Education Innovation Fund (HEIF).
16  *
17  * Portions of this software were developed by Mitchell Horne
18  * <mhorne@FreeBSD.org> under sponsorship from the FreeBSD Foundation.
19  *
20  * Redistribution and use in source and binary forms, with or without
21  * modification, are permitted provided that the following conditions
22  * are met:
23  * 1. Redistributions of source code must retain the above copyright
24  *    notice, this list of conditions and the following disclaimer.
25  * 2. Redistributions in binary form must reproduce the above copyright
26  *    notice, this list of conditions and the following disclaimer in the
27  *    documentation and/or other materials provided with the distribution.
28  *
29  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
30  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
31  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
32  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
33  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
34  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
35  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
36  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
37  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
38  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
39  * SUCH DAMAGE.
40  */
41 
42 #include "opt_platform.h"
43 
44 #include <sys/param.h>
45 #include <sys/systm.h>
46 #include <sys/ctype.h>
47 #include <sys/kernel.h>
48 #include <sys/pcpu.h>
49 #include <sys/sysctl.h>
50 
51 #include <machine/cpu.h>
52 #include <machine/cpufunc.h>
53 #include <machine/elf.h>
54 #include <machine/md_var.h>
55 #include <machine/thead.h>
56 #include <machine/cbo.h>
57 
58 #ifdef FDT
59 #include <dev/fdt/fdt_common.h>
60 #include <dev/ofw/openfirm.h>
61 #include <dev/ofw/ofw_bus_subr.h>
62 #endif
63 
64 const char machine[] = "riscv";
65 
66 SYSCTL_CONST_STRING(_hw, HW_MACHINE, machine, CTLFLAG_RD | CTLFLAG_CAPRD,
67     machine, "Machine class");
68 
69 /* Hardware implementation info. These values may be empty. */
70 register_t mvendorid;	/* The CPU's JEDEC vendor ID */
71 register_t marchid;	/* The architecture ID */
72 register_t mimpid;	/* The implementation ID */
73 
74 u_int mmu_caps;
75 
76 /* Supervisor-mode extension support. */
77 bool has_hyp;
78 bool has_vector;
79 bool __read_frequently has_sstc;
80 bool __read_frequently has_sscofpmf;
81 bool has_svpbmt;
82 bool has_svinval;
83 
84 /* Z-extensions support. */
85 bool has_zicbom;
86 bool has_zicboz;
87 bool has_zicbop;
88 
89 struct cpu_desc {
90 	const char	*cpu_mvendor_name;
91 	const char	*cpu_march_name;
92 	u_int		isa_extensions;		/* Single-letter extensions. */
93 	u_int		mmu_caps;
94 	u_int		smode_extensions;
95 #define	 SV_SSTC	(1 << 0)
96 #define	 SV_SVNAPOT	(1 << 1)
97 #define	 SV_SVPBMT	(1 << 2)
98 #define	 SV_SVINVAL	(1 << 3)
99 #define	 SV_SSCOFPMF	(1 << 4)
100 	u_int		z_extensions;		/* Multi-letter extensions. */
101 #define	 Z_ZICBOM	(1 << 0)
102 #define	 Z_ZICBOZ	(1 << 1)
103 #define	 Z_ZICBOP	(1 << 2)
104 	int		cbom_block_size;
105 	int		cboz_block_size;
106 };
107 
108 struct cpu_desc cpu_desc[MAXCPU];
109 
110 /*
111  * Micro-architecture tables.
112  */
113 struct marchid_entry {
114 	register_t	march_id;
115 	const char	*march_name;
116 };
117 
118 #define	MARCHID_END	{ -1ul, NULL }
119 
120 /* Open-source RISC-V architecture IDs; globally allocated. */
121 static const struct marchid_entry global_marchids[] = {
122 	{ MARCHID_UCB_ROCKET,	"UC Berkeley Rocket"		},
123 	{ MARCHID_UCB_BOOM,	"UC Berkeley Boom"		},
124 	{ MARCHID_UCB_SPIKE,	"UC Berkeley Spike"		},
125 	{ MARCHID_UCAM_RVBS,	"University of Cambridge RVBS"	},
126 	MARCHID_END
127 };
128 
129 static const struct marchid_entry sifive_marchids[] = {
130 	{ MARCHID_SIFIVE_U7,	"6/7/P200/X200-Series Processor" },
131 	{ MARCHID_SIFIVE_P5,	"P550/P650 Processor" },
132 	MARCHID_END
133 };
134 
135 static const struct marchid_entry spacemit_marchids[] = {
136 	{ MARCHID_SPACEMIT_K1,	"SpacemiT(R) X60" },
137 	MARCHID_END
138 };
139 
140 /*
141  * Known CPU vendor/manufacturer table.
142  */
143 static const struct {
144 	register_t			mvendor_id;
145 	const char			*mvendor_name;
146 	const struct marchid_entry	*marchid_table;
147 } mvendor_ids[] = {
148 	{ MVENDORID_UNIMPL,	"Unspecified",		NULL		},
149 	{ MVENDORID_SIFIVE,	"SiFive",		sifive_marchids	},
150 	{ MVENDORID_THEAD,	"T-Head",		NULL		},
151 	{ MVENDORID_SPACEMIT,	"SpacemiT",		spacemit_marchids	},
152 };
153 
154 /*
155  * The ISA string describes the complete set of instructions supported by a
156  * RISC-V CPU. The string begins with a small prefix (e.g. rv64) indicating the
157  * base ISA. It is followed first by single-letter ISA extensions, and then
158  * multi-letter ISA extensions.
159  *
160  * Underscores are used mainly to separate consecutive multi-letter extensions,
161  * but may optionally appear between any two extensions. An extension may be
162  * followed by a version number, in the form of 'Mpm', where M is the
163  * extension's major version number, and 'm' is the minor version number.
164  *
165  * The format is described in detail by the "ISA Extension Naming Conventions"
166  * chapter of the unprivileged spec.
167  */
168 #define	ISA_PREFIX		("rv" __XSTRING(__riscv_xlen))
169 #define	ISA_PREFIX_LEN		(sizeof(ISA_PREFIX) - 1)
170 
171 static __inline int
parse_ext_s(struct cpu_desc * desc,char * isa,int idx,int len)172 parse_ext_s(struct cpu_desc *desc, char *isa, int idx, int len)
173 {
174 #define	CHECK_S_EXT(str, flag)						\
175 	do {								\
176 		if (strncmp(&isa[idx], (str),				\
177 		    MIN(strlen(str), len - idx)) == 0) {		\
178 			desc->smode_extensions |= flag;			\
179 			return (idx + strlen(str));			\
180 		}							\
181 	} while (0)
182 
183 	/* Check for known/supported extensions. */
184 	CHECK_S_EXT("sstc",	SV_SSTC);
185 	CHECK_S_EXT("svnapot",	SV_SVNAPOT);
186 	CHECK_S_EXT("svpbmt",	SV_SVPBMT);
187 	CHECK_S_EXT("svinval",	SV_SVINVAL);
188 	CHECK_S_EXT("sscofpmf",	SV_SSCOFPMF);
189 
190 #undef CHECK_S_EXT
191 
192 	/*
193 	 * Proceed to the next multi-letter extension or the end of the
194 	 * string.
195 	 */
196 	while (isa[idx] != '_' && idx < len) {
197 		idx++;
198 	}
199 
200 	return (idx);
201 }
202 
203 static __inline int
parse_ext_x(struct cpu_desc * desc __unused,char * isa,int idx,int len)204 parse_ext_x(struct cpu_desc *desc __unused, char *isa, int idx, int len)
205 {
206 	/*
207 	 * Proceed to the next multi-letter extension or the end of the
208 	 * string.
209 	 */
210 	while (isa[idx] != '_' && idx < len) {
211 		idx++;
212 	}
213 
214 	return (idx);
215 }
216 
217 static __inline int
parse_ext_z(struct cpu_desc * desc __unused,char * isa,int idx,int len)218 parse_ext_z(struct cpu_desc *desc __unused, char *isa, int idx, int len)
219 {
220 #define	CHECK_Z_EXT(str, flag)						\
221 	do {								\
222 		if (strncmp(&isa[idx], (str),				\
223 		    MIN(strlen(str), len - idx)) == 0) {		\
224 			desc->z_extensions |= flag;			\
225 			return (idx + strlen(str));			\
226 		}							\
227 	} while (0)
228 
229 	/* Check for known/supported extensions. */
230 	CHECK_Z_EXT("zicbom",	Z_ZICBOM);
231 	CHECK_Z_EXT("zicboz",	Z_ZICBOZ);
232 	CHECK_Z_EXT("zicbop",	Z_ZICBOP);
233 
234 #undef CHECK_Z_EXT
235 	/*
236 	 * Proceed to the next multi-letter extension or the end of the
237 	 * string.
238 	 */
239 	while (isa[idx] != '_' && idx < len) {
240 		idx++;
241 	}
242 
243 	return (idx);
244 }
245 
246 static __inline int
parse_ext_version(char * isa,int idx,u_int * majorp __unused,u_int * minorp __unused)247 parse_ext_version(char *isa, int idx, u_int *majorp __unused,
248     u_int *minorp __unused)
249 {
250 	/* Major version. */
251 	while (isdigit(isa[idx]))
252 		idx++;
253 
254 	if (isa[idx] != 'p')
255 		return (idx);
256 	else
257 		idx++;
258 
259 	/* Minor version. */
260 	while (isdigit(isa[idx]))
261 		idx++;
262 
263 	return (idx);
264 }
265 
266 /*
267  * Parse the ISA string, building up the set of HWCAP bits as they are found.
268  */
269 static int
parse_riscv_isa(struct cpu_desc * desc,char * isa,int len)270 parse_riscv_isa(struct cpu_desc *desc, char *isa, int len)
271 {
272 	int i;
273 
274 	/* Check the string prefix. */
275 	if (strncmp(isa, ISA_PREFIX, ISA_PREFIX_LEN) != 0) {
276 		printf("%s: Unrecognized ISA string: %s\n", __func__, isa);
277 		return (-1);
278 	}
279 
280 	i = ISA_PREFIX_LEN;
281 	while (i < len) {
282 		switch(isa[i]) {
283 		case 'a':
284 		case 'b':
285 		case 'c':
286 		case 'd':
287 		case 'f':
288 		case 'h':
289 		case 'i':
290 		case 'm':
291 		case 'v':
292 			desc->isa_extensions |= HWCAP_ISA_BIT(isa[i]);
293 			i++;
294 			break;
295 		case 'g':
296 			desc->isa_extensions |= HWCAP_ISA_G;
297 			i++;
298 			break;
299 		case 's':
300 			/*
301 			 * XXX: older versions of this string erroneously
302 			 * indicated supervisor and user mode support as
303 			 * single-letter extensions. Detect and skip both 's'
304 			 * and 'u'.
305 			 */
306 			if (isa[i - 1] != '_' && isa[i + 1] == 'u') {
307 				i += 2;
308 				continue;
309 			}
310 
311 			/*
312 			 * Supervisor-level extension namespace.
313 			 */
314 			i = parse_ext_s(desc, isa, i, len);
315 			break;
316 		case 'x':
317 			/*
318 			 * Custom extension namespace. For now, we ignore
319 			 * these.
320 			 */
321 			i = parse_ext_x(desc, isa, i, len);
322 			break;
323 		case 'z':
324 			/*
325 			 * Multi-letter standard extension namespace.
326 			 */
327 			i = parse_ext_z(desc, isa, i, len);
328 			break;
329 		case '_':
330 			i++;
331 			continue;
332 		default:
333 			/* Unrecognized/unsupported. */
334 			i++;
335 			break;
336 		}
337 
338 		i = parse_ext_version(isa, i, NULL, NULL);
339 	}
340 
341 	return (0);
342 }
343 
344 #ifdef FDT
345 static void
parse_mmu_fdt(struct cpu_desc * desc,phandle_t node)346 parse_mmu_fdt(struct cpu_desc *desc, phandle_t node)
347 {
348 	char mmu[16];
349 
350 	desc->mmu_caps |= MMU_SV39;
351 	if (OF_getprop(node, "mmu-type", mmu, sizeof(mmu)) > 0) {
352 		if (strcmp(mmu, "riscv,sv48") == 0)
353 			desc->mmu_caps |= MMU_SV48;
354 		else if (strcmp(mmu, "riscv,sv57") == 0)
355 			desc->mmu_caps |= MMU_SV48 | MMU_SV57;
356 	}
357 }
358 
359 static void
parse_cbo_fdt(struct cpu_desc * desc,phandle_t node)360 parse_cbo_fdt(struct cpu_desc *desc, phandle_t node)
361 {
362 	int error;
363 
364 	error = OF_getencprop(node, "riscv,cbom-block-size",
365 	    &desc->cbom_block_size, sizeof(desc->cbom_block_size));
366 	if (error == -1)
367 		desc->cbom_block_size = 0;
368 
369 	error = OF_getencprop(node, "riscv,cboz-block-size",
370 	    &desc->cboz_block_size, sizeof(desc->cboz_block_size));
371 	if (error == -1)
372 		desc->cboz_block_size = 0;
373 }
374 
375 static void
identify_cpu_features_fdt(u_int cpu,struct cpu_desc * desc)376 identify_cpu_features_fdt(u_int cpu, struct cpu_desc *desc)
377 {
378 	char isa[1024];
379 	phandle_t node;
380 	ssize_t len;
381 	pcell_t reg;
382 	u_int hart;
383 
384 	node = OF_finddevice("/cpus");
385 	if (node == -1) {
386 		printf("%s: could not find /cpus node in FDT\n", __func__);
387 		return;
388 	}
389 
390 	hart = pcpu_find(cpu)->pc_hart;
391 
392 	/*
393 	 * Locate our current CPU's node in the device-tree, and parse its
394 	 * contents to detect supported CPU/ISA features and extensions.
395 	 */
396 	for (node = OF_child(node); node > 0; node = OF_peer(node)) {
397 		/* Skip any non-CPU nodes, such as cpu-map. */
398 		if (!ofw_bus_node_is_compatible(node, "riscv"))
399 			continue;
400 
401 		/* Find this CPU */
402 		if (OF_getencprop(node, "reg", &reg, sizeof(reg)) <= 0 ||
403 		    reg != hart)
404 			continue;
405 
406 		len = OF_getprop(node, "riscv,isa", isa, sizeof(isa));
407 		KASSERT(len <= sizeof(isa), ("ISA string truncated"));
408 		if (len == -1) {
409 			printf("%s: could not find 'riscv,isa' property "
410 			    "for CPU %d, hart %u\n", __func__, cpu, hart);
411 			return;
412 		}
413 
414 		if (bootverbose)
415 			printf("hart %d isa: %s\n", hart, isa);
416 
417 		/*
418 		 * The string is specified to be lowercase, but let's be
419 		 * certain.
420 		 */
421 		for (int i = 0; i < len; i++)
422 			isa[i] = tolower(isa[i]);
423 		if (parse_riscv_isa(desc, isa, len) != 0)
424 			return;
425 
426 		/* Check MMU features. */
427 		parse_mmu_fdt(desc, node);
428 
429 		/* Cache-block operations (CBO). */
430 		parse_cbo_fdt(desc, node);
431 
432 		/* We are done. */
433 		break;
434 	}
435 	if (node <= 0) {
436 		printf("%s: could not find FDT node for CPU %u, hart %u\n",
437 		    __func__, cpu, hart);
438 	}
439 }
440 #endif
441 
442 static void
identify_cpu_features(u_int cpu,struct cpu_desc * desc)443 identify_cpu_features(u_int cpu, struct cpu_desc *desc)
444 {
445 #ifdef FDT
446 	identify_cpu_features_fdt(cpu, desc);
447 #endif
448 }
449 
450 /*
451  * Update kernel/user global state based on the feature parsing results, stored
452  * in desc.
453  *
454  * We keep only the subset of values common to all CPUs.
455  */
456 static void
update_global_capabilities(u_int cpu,struct cpu_desc * desc)457 update_global_capabilities(u_int cpu, struct cpu_desc *desc)
458 {
459 #define UPDATE_CAP(t, v)				\
460 	do {						\
461 		if (cpu == 0) {				\
462 			(t) = (v);			\
463 		} else {				\
464 			(t) &= (v);			\
465 		}					\
466 	} while (0)
467 
468 	/* Update the capabilities exposed to userspace via AT_HWCAP. */
469 	UPDATE_CAP(elf_hwcap, (u_long)desc->isa_extensions);
470 
471 	/*
472 	 * MMU capabilities, e.g. Sv48.
473 	 */
474 	UPDATE_CAP(mmu_caps, desc->mmu_caps);
475 
476 	/* Supervisor-mode extension support. */
477 	UPDATE_CAP(has_vector, (desc->isa_extensions & HWCAP_ISA_V) != 0);
478 	UPDATE_CAP(has_hyp, (desc->isa_extensions & HWCAP_ISA_H) != 0);
479 	UPDATE_CAP(has_sstc, (desc->smode_extensions & SV_SSTC) != 0);
480 	UPDATE_CAP(has_sscofpmf, (desc->smode_extensions & SV_SSCOFPMF) != 0);
481 	UPDATE_CAP(has_svpbmt, (desc->smode_extensions & SV_SVPBMT) != 0);
482 	UPDATE_CAP(has_svinval, (desc->smode_extensions & SV_SVINVAL) != 0);
483 
484 	/* Z extension support. */
485 	UPDATE_CAP(has_zicbom, (desc->z_extensions & Z_ZICBOM) != 0);
486 	UPDATE_CAP(has_zicboz, (desc->z_extensions & Z_ZICBOZ) != 0);
487 	UPDATE_CAP(has_zicbop, (desc->z_extensions & Z_ZICBOP) != 0);
488 
489 #undef UPDATE_CAP
490 }
491 
492 static void
identify_cpu_ids(struct cpu_desc * desc)493 identify_cpu_ids(struct cpu_desc *desc)
494 {
495 	const struct marchid_entry *table = NULL;
496 	int i;
497 
498 	desc->cpu_mvendor_name = "Unknown";
499 	desc->cpu_march_name = "Unknown";
500 
501 	/*
502 	 * Search for a recognized vendor, and possibly obtain the secondary
503 	 * table for marchid lookup.
504 	 */
505 	for (i = 0; i < nitems(mvendor_ids); i++) {
506 		if (mvendorid == mvendor_ids[i].mvendor_id) {
507 			desc->cpu_mvendor_name = mvendor_ids[i].mvendor_name;
508 			table = mvendor_ids[i].marchid_table;
509 			break;
510 		}
511 	}
512 
513 	if (marchid == MARCHID_UNIMPL) {
514 		desc->cpu_march_name = "Unspecified";
515 		return;
516 	}
517 
518 	if (MARCHID_IS_OPENSOURCE(marchid)) {
519 		table = global_marchids;
520 	} else if (table == NULL)
521 		return;
522 
523 	for (i = 0; table[i].march_name != NULL; i++) {
524 		if (marchid == table[i].march_id) {
525 			desc->cpu_march_name = table[i].march_name;
526 			break;
527 		}
528 	}
529 }
530 
531 static void
handle_thead_quirks(u_int cpu,struct cpu_desc * desc)532 handle_thead_quirks(u_int cpu, struct cpu_desc *desc)
533 {
534 	if (cpu != 0)
535 		return;
536 
537 	/*
538 	 * For now, it is assumed that T-HEAD CPUs have both marchid and mimpid
539 	 * values of zero (although we leave this unchecked). It is true in
540 	 * practice for the early generations of this hardware (C906, C910,
541 	 * C920). In the future, the identity checks may need to become more
542 	 * granular, but until then all known T-HEAD quirks are applied
543 	 * indiscriminantly.
544 	 *
545 	 * Note: any changes in this function relating to has_errata_thead_pbmt
546 	 * may need to be applied to get_pte_fixup_bits (in locore.S) as well.
547 	 */
548 
549 	has_errata_thead_pbmt = true;
550 	thead_setup_cache();
551 }
552 
553 static void
handle_cpu_quirks(u_int cpu,struct cpu_desc * desc)554 handle_cpu_quirks(u_int cpu, struct cpu_desc *desc)
555 {
556 	switch (mvendorid) {
557 	case MVENDORID_THEAD:
558 		handle_thead_quirks(cpu, desc);
559 		break;
560 	}
561 }
562 
563 void
identify_cpu(u_int cpu)564 identify_cpu(u_int cpu)
565 {
566 	struct cpu_desc *desc = &cpu_desc[cpu];
567 
568 	identify_cpu_ids(desc);
569 	identify_cpu_features(cpu, desc);
570 
571 	update_global_capabilities(cpu, desc);
572 	handle_cpu_quirks(cpu, desc);
573 
574 	if (has_zicbom && cpu == 0)
575 		cbo_zicbom_setup_cache(desc->cbom_block_size);
576 }
577 
578 void
printcpuinfo(u_int cpu)579 printcpuinfo(u_int cpu)
580 {
581 	struct cpu_desc *desc;
582 	u_int hart;
583 
584 	desc = &cpu_desc[cpu];
585 	hart = pcpu_find(cpu)->pc_hart;
586 
587 	/* XXX: check this here so we are guaranteed to have console output. */
588 	KASSERT(desc->isa_extensions != 0,
589 	    ("Empty extension set for CPU %u, did parsing fail?", cpu));
590 
591 	/*
592 	 * Suppress the output of some fields in the common case of identical
593 	 * CPU features.
594 	 */
595 #define	SHOULD_PRINT(_field)	\
596     (cpu == 0 || desc[0]._field != desc[-1]._field)
597 
598 	/* Always print summary line. */
599 	printf("CPU %-3u: Vendor=%s Core=%s (Hart %u)\n", cpu,
600 	    desc->cpu_mvendor_name, desc->cpu_march_name, hart);
601 
602 	/* These values are global. */
603 	if (cpu == 0)
604 		printf("  marchid=%#lx, mimpid=%#lx\n", marchid, mimpid);
605 
606 	if (SHOULD_PRINT(mmu_caps)) {
607 		printf("  MMU: %#b\n", desc->mmu_caps,
608 		    "\020"
609 		    "\01Sv39"
610 		    "\02Sv48"
611 		    "\03Sv57");
612 	}
613 
614 	if (SHOULD_PRINT(isa_extensions)) {
615 		printf("  ISA: %#b\n", desc->isa_extensions,
616 		    "\020"
617 		    "\01Atomic"
618 		    "\03Compressed"
619 		    "\04Double"
620 		    "\06Float"
621 		    "\10Hypervisor"
622 		    "\15Mult/Div"
623 		    "\26Vector");
624 	}
625 
626 	if (SHOULD_PRINT(smode_extensions)) {
627 		printf("  S-mode Extensions: %#b\n", desc->smode_extensions,
628 		    "\020"
629 		    "\01Sstc"
630 		    "\02Svnapot"
631 		    "\03Svpbmt"
632 		    "\04Svinval"
633 		    "\05Sscofpmf");
634 	}
635 
636 #undef SHOULD_PRINT
637 }
638