1 /*
2 * Copyright (c) 2006, 2025, Oracle and/or its affiliates. All rights reserved.
3 * Copyright (c) 2014, 2020, Red Hat Inc. All rights reserved.
4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 *
6 * This code is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License version 2 only, as
8 * published by the Free Software Foundation.
9 *
10 * This code is distributed in the hope that it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * version 2 for more details (a copy is included in the LICENSE file that
14 * accompanied this code).
15 *
16 * You should have received a copy of the GNU General Public License version
17 * 2 along with this work; if not, write to the Free Software Foundation,
18 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
19 *
20 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
21 * or visit www.oracle.com if you need additional information or have any
22 * questions.
23 *
24 */
25
26 #include "runtime/os.hpp"
27 #include "runtime/os.inline.hpp"
28 #include "runtime/vm_version.hpp"
29
30 #include <asm/hwcap.h>
31 #include <sys/auxv.h>
32 #include <sys/prctl.h>
33
34 #ifndef HWCAP_AES
35 #define HWCAP_AES (1<<3)
36 #endif
37
38 #ifndef HWCAP_PMULL
39 #define HWCAP_PMULL (1<<4)
40 #endif
41
42 #ifndef HWCAP_SHA1
43 #define HWCAP_SHA1 (1<<5)
44 #endif
45
46 #ifndef HWCAP_SHA2
47 #define HWCAP_SHA2 (1<<6)
48 #endif
49
50 #ifndef HWCAP_CRC32
51 #define HWCAP_CRC32 (1<<7)
52 #endif
53
54 #ifndef HWCAP_ATOMICS
55 #define HWCAP_ATOMICS (1<<8)
56 #endif
57
58 #ifndef HWCAP_DCPOP
59 #define HWCAP_DCPOP (1<<16)
60 #endif
61
62 #ifndef HWCAP_SHA3
63 #define HWCAP_SHA3 (1 << 17)
64 #endif
65
66 #ifndef HWCAP_SHA512
67 #define HWCAP_SHA512 (1 << 21)
68 #endif
69
70 #ifndef HWCAP_SVE
71 #define HWCAP_SVE (1 << 22)
72 #endif
73
74 #ifndef HWCAP_SB
75 #define HWCAP_SB (1 << 29)
76 #endif
77
78 #ifndef HWCAP_PACA
79 #define HWCAP_PACA (1 << 30)
80 #endif
81
82 #ifndef HWCAP_FPHP
83 #define HWCAP_FPHP (1<<9)
84 #endif
85
86 #ifndef HWCAP_ASIMDHP
87 #define HWCAP_ASIMDHP (1<<10)
88 #endif
89
90 #ifndef HWCAP2_SVE2
91 #define HWCAP2_SVE2 (1 << 1)
92 #endif
93
94 #ifndef HWCAP2_SVEBITPERM
95 #define HWCAP2_SVEBITPERM (1 << 4)
96 #endif
97
98 #ifndef PR_SVE_GET_VL
99 // For old toolchains which do not have SVE related macros defined.
100 #define PR_SVE_SET_VL 50
101 #define PR_SVE_GET_VL 51
102 #endif
103
104 int VM_Version::get_current_sve_vector_length() {
105 assert(VM_Version::supports_sve(), "should not call this");
106 return prctl(PR_SVE_GET_VL);
107 }
108
109 int VM_Version::set_and_get_current_sve_vector_length(int length) {
110 assert(VM_Version::supports_sve(), "should not call this");
111 int new_length = prctl(PR_SVE_SET_VL, length);
112 return new_length;
113 }
114
115 void VM_Version::get_os_cpu_info() {
116
117 uint64_t auxv = getauxval(AT_HWCAP);
118 uint64_t auxv2 = getauxval(AT_HWCAP2);
119
120 static_assert(CPU_FP == HWCAP_FP, "Flag CPU_FP must follow Linux HWCAP");
121 static_assert(CPU_ASIMD == HWCAP_ASIMD, "Flag CPU_ASIMD must follow Linux HWCAP");
122 static_assert(CPU_EVTSTRM == HWCAP_EVTSTRM, "Flag CPU_EVTSTRM must follow Linux HWCAP");
123 static_assert(CPU_AES == HWCAP_AES, "Flag CPU_AES must follow Linux HWCAP");
124 static_assert(CPU_PMULL == HWCAP_PMULL, "Flag CPU_PMULL must follow Linux HWCAP");
125 static_assert(CPU_SHA1 == HWCAP_SHA1, "Flag CPU_SHA1 must follow Linux HWCAP");
126 static_assert(CPU_SHA2 == HWCAP_SHA2, "Flag CPU_SHA2 must follow Linux HWCAP");
127 static_assert(CPU_CRC32 == HWCAP_CRC32, "Flag CPU_CRC32 must follow Linux HWCAP");
128 static_assert(CPU_LSE == HWCAP_ATOMICS, "Flag CPU_LSE must follow Linux HWCAP");
129 static_assert(CPU_DCPOP == HWCAP_DCPOP, "Flag CPU_DCPOP must follow Linux HWCAP");
130 static_assert(CPU_SHA3 == HWCAP_SHA3, "Flag CPU_SHA3 must follow Linux HWCAP");
131 static_assert(CPU_SHA512 == HWCAP_SHA512, "Flag CPU_SHA512 must follow Linux HWCAP");
132 static_assert(CPU_SVE == HWCAP_SVE, "Flag CPU_SVE must follow Linux HWCAP");
133 static_assert(CPU_PACA == HWCAP_PACA, "Flag CPU_PACA must follow Linux HWCAP");
134 static_assert(CPU_FPHP == HWCAP_FPHP, "Flag CPU_FPHP must follow Linux HWCAP");
135 static_assert(CPU_ASIMDHP == HWCAP_ASIMDHP, "Flag CPU_ASIMDHP must follow Linux HWCAP");
136 _features = auxv & (
137 HWCAP_FP |
138 HWCAP_ASIMD |
139 HWCAP_EVTSTRM |
140 HWCAP_AES |
141 HWCAP_PMULL |
142 HWCAP_SHA1 |
143 HWCAP_SHA2 |
144 HWCAP_CRC32 |
145 HWCAP_ATOMICS |
146 HWCAP_DCPOP |
147 HWCAP_SHA3 |
148 HWCAP_SHA512 |
149 HWCAP_SVE |
150 HWCAP_SB |
151 HWCAP_PACA |
152 HWCAP_FPHP |
153 HWCAP_ASIMDHP);
154
155 if (auxv2 & HWCAP2_SVE2) _features |= CPU_SVE2;
156 if (auxv2 & HWCAP2_SVEBITPERM) _features |= CPU_SVEBITPERM;
157
158 uint64_t ctr_el0;
159 uint64_t dczid_el0;
160 __asm__ (
161 "mrs %0, CTR_EL0\n"
162 "mrs %1, DCZID_EL0\n"
163 : "=r"(ctr_el0), "=r"(dczid_el0)
164 );
165
166 _icache_line_size = (1 << (ctr_el0 & 0x0f)) * 4;
167 _dcache_line_size = (1 << ((ctr_el0 >> 16) & 0x0f)) * 4;
168
169 if (!(dczid_el0 & 0x10)) {
170 _zva_length = 4 << (dczid_el0 & 0xf);
171 }
172
173 if (FILE *f = os::fopen("/proc/cpuinfo", "r")) {
174 // need a large buffer as the flags line may include lots of text
175 char buf[1024], *p;
176 while (fgets(buf, sizeof (buf), f) != nullptr) {
177 if ((p = strchr(buf, ':')) != nullptr) {
178 long v = strtol(p+1, nullptr, 0);
179 if (strncmp(buf, "CPU implementer", sizeof "CPU implementer" - 1) == 0) {
180 _cpu = v;
181 } else if (strncmp(buf, "CPU variant", sizeof "CPU variant" - 1) == 0) {
182 _variant = v;
183 } else if (strncmp(buf, "CPU part", sizeof "CPU part" - 1) == 0) {
184 if (_model != v) _model2 = _model;
185 _model = v;
186 } else if (strncmp(buf, "CPU revision", sizeof "CPU revision" - 1) == 0) {
187 _revision = v;
188 } else if (strncmp(buf, "flags", sizeof("flags") - 1) == 0) {
189 if (strstr(p+1, "dcpop")) {
190 guarantee(_features & CPU_DCPOP, "dcpop availability should be consistent");
191 }
192 }
193 }
194 }
195 fclose(f);
196 }
197 }
198
199 static bool read_fully(const char *fname, char *buf, size_t buflen) {
200 assert(buf != nullptr, "invalid argument");
201 assert(buflen >= 1, "invalid argument");
202 int fd = os::open(fname, O_RDONLY, 0);
203 if (fd != -1) {
204 PRAGMA_DIAG_PUSH
205 PRAGMA_NONNULL_IGNORED
206 // Suppress false positive gcc warning, which may be an example of
207 // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=87489
208 // The warning also hasn't been seen with vanilla gcc release, so may also
209 // involve some distro-specific gcc patch.
210 ssize_t read_sz = ::read(fd, buf, buflen);
211 PRAGMA_DIAG_POP
212 ::close(fd);
213
214 // Skip if the contents is just "\n" because some machine only sets
215 // '\n' to the board name.
216 // (e.g. esys/devices/virtual/dmi/id/board_name)
217 if (read_sz > 0 && !(read_sz == 1 && *buf == '\n')) {
218 // Replace '\0' to ' '
219 for (char *ch = buf; ch < buf + read_sz - 1; ch++) {
220 if (*ch == '\0') {
221 *ch = ' ';
222 }
223 }
224 buf[read_sz - 1] = '\0';
225 return true;
226 }
227 }
228 *buf = '\0';
229 return false;
230 }
231
232 void VM_Version::get_compatible_board(char *buf, int buflen) {
233 const char *board_name_file_list[] = {
234 "/proc/device-tree/compatible",
235 "/sys/devices/virtual/dmi/id/board_name",
236 "/sys/devices/virtual/dmi/id/product_name",
237 nullptr
238 };
239
240 for (const char **fname = board_name_file_list; *fname != nullptr; fname++) {
241 if (read_fully(*fname, buf, buflen)) {
242 return;
243 }
244 }
245 }