• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Only enable the C standard library hwprobe interface on Android for now.
3  * Patches to add a compatible hwprobe API to glibc are available but not
4  * merged at the time of writing and so cannot easily be tested.  The
5  * #ifdef __ANDROID__ check will be removed in the future.
6  */
7 #ifdef __ANDROID__
8 #ifdef __has_include
9 #if __has_include(<sys/hwprobe.h>)
10 #define CPUINFO_RISCV_LINUX_HAVE_C_HWPROBE
11 #include <sys/hwprobe.h>
12 #endif
13 #endif
14 #endif
15 
16 #include <sched.h>
17 
18 #include <cpuinfo/log.h>
19 #include <riscv/api.h>
20 #include <riscv/linux/api.h>
21 
22 #ifndef CPUINFO_RISCV_LINUX_HAVE_C_HWPROBE
23 
24 #include <stdint.h>
25 #include <sys/syscall.h>
26 #include <unistd.h>
27 
28 struct riscv_hwprobe {
29 	int64_t key;
30 	uint64_t value;
31 };
32 
33 /*
34  * The standard C library our binary was compiled with does not support
35  * hwprobe but the kernel on which we are running might do.  The
36  * constants below are copied from
37  * /usr/include/riscv64-linux-gnu/asm/hwprobe.h.  They allow us to
38  * invoke the hwprobe syscall directly.  We duplicate the constants
39  * rather than including the kernel hwprobe.h header, as this header
40  * will only be present if we're building Linux 6.4 or greater.
41  */
42 
43 #define RISCV_HWPROBE_KEY_MVENDORID 0
44 #define RISCV_HWPROBE_KEY_MARCHID 1
45 #define RISCV_HWPROBE_KEY_MIMPID 2
46 #define RISCV_HWPROBE_KEY_BASE_BEHAVIOR 3
47 #define RISCV_HWPROBE_BASE_BEHAVIOR_IMA (1 << 0)
48 #define RISCV_HWPROBE_KEY_IMA_EXT_0 4
49 #define RISCV_HWPROBE_IMA_FD (1 << 0)
50 #define RISCV_HWPROBE_IMA_C (1 << 1)
51 #define RISCV_HWPROBE_IMA_V (1 << 2)
52 #define RISCV_HWPROBE_EXT_ZBA (1 << 3)
53 #define RISCV_HWPROBE_EXT_ZBB (1 << 4)
54 #define RISCV_HWPROBE_EXT_ZBS (1 << 5)
55 #define RISCV_HWPROBE_EXT_ZICBOZ (1 << 6)
56 #define RISCV_HWPROBE_KEY_CPUPERF_0 5
57 #define RISCV_HWPROBE_MISALIGNED_UNKNOWN (0 << 0)
58 #define RISCV_HWPROBE_MISALIGNED_EMULATED (1 << 0)
59 #define RISCV_HWPROBE_MISALIGNED_SLOW (2 << 0)
60 #define RISCV_HWPROBE_MISALIGNED_FAST (3 << 0)
61 #define RISCV_HWPROBE_MISALIGNED_UNSUPPORTED (4 << 0)
62 #define RISCV_HWPROBE_MISALIGNED_MASK (7 << 0)
63 
64 #ifndef NR_riscv_hwprobe
65 #ifndef NR_arch_specific_syscall
66 #define NR_arch_specific_syscall 244
67 #endif
68 #define NR_riscv_hwprobe (NR_arch_specific_syscall + 14)
69 #endif
70 #endif
71 
cpuinfo_riscv_linux_decode_vendor_uarch_from_hwprobe(uint32_t processor,enum cpuinfo_vendor vendor[restrict static1],enum cpuinfo_uarch uarch[restrict static1])72 void cpuinfo_riscv_linux_decode_vendor_uarch_from_hwprobe(
73 	uint32_t processor,
74 	enum cpuinfo_vendor vendor[restrict static 1],
75 	enum cpuinfo_uarch uarch[restrict static 1]) {
76 	struct riscv_hwprobe pairs[] = {
77 		{
78 			.key = RISCV_HWPROBE_KEY_MVENDORID,
79 		},
80 		{
81 			.key = RISCV_HWPROBE_KEY_MARCHID,
82 		},
83 		{
84 			.key = RISCV_HWPROBE_KEY_MIMPID,
85 		},
86 	};
87 	const size_t pairs_count = sizeof(pairs) / sizeof(struct riscv_hwprobe);
88 
89 	/* In case of failure, report unknown. */
90 	*vendor = cpuinfo_vendor_unknown;
91 	*uarch = cpuinfo_uarch_unknown;
92 
93 	/* Create a CPU set with this processor flagged. */
94 	const size_t cpu_count = processor + 1;
95 	cpu_set_t* cpu_set = CPU_ALLOC(cpu_count);
96 	if (cpu_set == NULL) {
97 		cpuinfo_log_warning("failed to allocate space for cpu_set");
98 		return;
99 	}
100 
101 	const size_t cpu_set_size = CPU_ALLOC_SIZE(cpu_count);
102 	CPU_ZERO_S(cpu_set_size, cpu_set);
103 	CPU_SET_S(processor, cpu_set_size, cpu_set);
104 
105 	/* Request all available information from hwprobe. */
106 #ifndef CPUINFO_RISCV_LINUX_HAVE_C_HWPROBE
107 	/*
108 	 * No standard library support for hwprobe.  We'll need to invoke the
109 	 * syscall directly.  See
110 	 *
111 	 * https://docs.kernel.org/arch/riscv/hwprobe.html
112 	 *
113 	 * for more details.
114 	 */
115 	int ret = syscall(NR_riscv_hwprobe, pairs, pairs_count, cpu_set_size, (unsigned long*)cpu_set, 0 /* flags */);
116 #else
117 	int ret = __riscv_hwprobe(pairs, pairs_count, cpu_set_size, (unsigned long*)cpu_set, 0 /* flags */);
118 #endif
119 	if (ret < 0) {
120 		cpuinfo_log_warning("failed to get hwprobe information, err: %d", ret);
121 		goto cleanup;
122 	}
123 
124 	/**
125 	 * The syscall may not have populated all requested keys, loop through
126 	 * the list and store the values that were discovered.
127 	 */
128 	uint32_t vendor_id = 0;
129 	uint32_t arch_id = 0;
130 	uint32_t imp_id = 0;
131 	for (size_t pair = 0; pair < pairs_count; pair++) {
132 		switch (pairs[pair].key) {
133 			case RISCV_HWPROBE_KEY_MVENDORID:
134 				vendor_id = pairs[pair].value;
135 				break;
136 			case RISCV_HWPROBE_KEY_MARCHID:
137 				arch_id = pairs[pair].value;
138 				break;
139 			case RISCV_HWPROBE_KEY_MIMPID:
140 				imp_id = pairs[pair].value;
141 				break;
142 			default:
143 				/* The key value may be -1 if unsupported. */
144 				break;
145 		}
146 	}
147 	cpuinfo_riscv_decode_vendor_uarch(vendor_id, arch_id, imp_id, vendor, uarch);
148 
149 cleanup:
150 	CPU_FREE(cpu_set);
151 }
152