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_PACA
75 #define HWCAP_PACA (1 << 30)
76 #endif
77
78 #ifndef HWCAP_FPHP
79 #define HWCAP_FPHP (1<<9)
80 #endif
81
82 #ifndef HWCAP_ASIMDHP
83 #define HWCAP_ASIMDHP (1<<10)
84 #endif
85
86 #ifndef HWCAP2_SVE2
87 #define HWCAP2_SVE2 (1 << 1)
88 #endif
89
90 #ifndef HWCAP2_SVEBITPERM
91 #define HWCAP2_SVEBITPERM (1 << 4)
92 #endif
93
94 #ifndef PR_SVE_GET_VL
95 // For old toolchains which do not have SVE related macros defined.
96 #define PR_SVE_SET_VL 50
97 #define PR_SVE_GET_VL 51
98 #endif
99
100 int VM_Version::get_current_sve_vector_length() {
101 assert(VM_Version::supports_sve(), "should not call this");
102 return prctl(PR_SVE_GET_VL);
103 }
104
105 int VM_Version::set_and_get_current_sve_vector_length(int length) {
106 assert(VM_Version::supports_sve(), "should not call this");
107 int new_length = prctl(PR_SVE_SET_VL, length);
108 return new_length;
109 }
110
111 void VM_Version::get_os_cpu_info() {
112
113 uint64_t auxv = getauxval(AT_HWCAP);
114 uint64_t auxv2 = getauxval(AT_HWCAP2);
115
116 static_assert(CPU_FP == HWCAP_FP, "Flag CPU_FP must follow Linux HWCAP");
117 static_assert(CPU_ASIMD == HWCAP_ASIMD, "Flag CPU_ASIMD must follow Linux HWCAP");
118 static_assert(CPU_EVTSTRM == HWCAP_EVTSTRM, "Flag CPU_EVTSTRM must follow Linux HWCAP");
119 static_assert(CPU_AES == HWCAP_AES, "Flag CPU_AES must follow Linux HWCAP");
120 static_assert(CPU_PMULL == HWCAP_PMULL, "Flag CPU_PMULL must follow Linux HWCAP");
121 static_assert(CPU_SHA1 == HWCAP_SHA1, "Flag CPU_SHA1 must follow Linux HWCAP");
122 static_assert(CPU_SHA2 == HWCAP_SHA2, "Flag CPU_SHA2 must follow Linux HWCAP");
123 static_assert(CPU_CRC32 == HWCAP_CRC32, "Flag CPU_CRC32 must follow Linux HWCAP");
124 static_assert(CPU_LSE == HWCAP_ATOMICS, "Flag CPU_LSE must follow Linux HWCAP");
125 static_assert(CPU_DCPOP == HWCAP_DCPOP, "Flag CPU_DCPOP must follow Linux HWCAP");
126 static_assert(CPU_SHA3 == HWCAP_SHA3, "Flag CPU_SHA3 must follow Linux HWCAP");
127 static_assert(CPU_SHA512 == HWCAP_SHA512, "Flag CPU_SHA512 must follow Linux HWCAP");
128 static_assert(CPU_SVE == HWCAP_SVE, "Flag CPU_SVE must follow Linux HWCAP");
129 static_assert(CPU_PACA == HWCAP_PACA, "Flag CPU_PACA must follow Linux HWCAP");
130 static_assert(CPU_FPHP == HWCAP_FPHP, "Flag CPU_FPHP must follow Linux HWCAP");
131 static_assert(CPU_ASIMDHP == HWCAP_ASIMDHP, "Flag CPU_ASIMDHP must follow Linux HWCAP");
132 _features = auxv & (
133 HWCAP_FP |
134 HWCAP_ASIMD |
135 HWCAP_EVTSTRM |
136 HWCAP_AES |
137 HWCAP_PMULL |
138 HWCAP_SHA1 |
139 HWCAP_SHA2 |
140 HWCAP_CRC32 |
141 HWCAP_ATOMICS |
142 HWCAP_DCPOP |
143 HWCAP_SHA3 |
144 HWCAP_SHA512 |
145 HWCAP_SVE |
146 HWCAP_PACA |
147 HWCAP_FPHP |
148 HWCAP_ASIMDHP);
149
150 if (auxv2 & HWCAP2_SVE2) _features |= CPU_SVE2;
151 if (auxv2 & HWCAP2_SVEBITPERM) _features |= CPU_SVEBITPERM;
152
153 uint64_t ctr_el0;
154 uint64_t dczid_el0;
155 __asm__ (
156 "mrs %0, CTR_EL0\n"
157 "mrs %1, DCZID_EL0\n"
158 : "=r"(ctr_el0), "=r"(dczid_el0)
159 );
160
161 _icache_line_size = (1 << (ctr_el0 & 0x0f)) * 4;
162 _dcache_line_size = (1 << ((ctr_el0 >> 16) & 0x0f)) * 4;
163
164 if (!(dczid_el0 & 0x10)) {
165 _zva_length = 4 << (dczid_el0 & 0xf);
166 }
167
168 if (FILE *f = os::fopen("/proc/cpuinfo", "r")) {
169 // need a large buffer as the flags line may include lots of text
170 char buf[1024], *p;
171 while (fgets(buf, sizeof (buf), f) != nullptr) {
172 if ((p = strchr(buf, ':')) != nullptr) {
173 long v = strtol(p+1, nullptr, 0);
174 if (strncmp(buf, "CPU implementer", sizeof "CPU implementer" - 1) == 0) {
175 _cpu = v;
176 } else if (strncmp(buf, "CPU variant", sizeof "CPU variant" - 1) == 0) {
177 _variant = v;
178 } else if (strncmp(buf, "CPU part", sizeof "CPU part" - 1) == 0) {
179 if (_model != v) _model2 = _model;
180 _model = v;
181 } else if (strncmp(buf, "CPU revision", sizeof "CPU revision" - 1) == 0) {
182 _revision = v;
183 } else if (strncmp(buf, "flags", sizeof("flags") - 1) == 0) {
184 if (strstr(p+1, "dcpop")) {
185 guarantee(_features & CPU_DCPOP, "dcpop availability should be consistent");
186 }
187 }
188 }
189 }
190 fclose(f);
191 }
192 }
193
194 static bool read_fully(const char *fname, char *buf, size_t buflen) {
195 assert(buf != nullptr, "invalid argument");
196 assert(buflen >= 1, "invalid argument");
197 int fd = os::open(fname, O_RDONLY, 0);
198 if (fd != -1) {
199 PRAGMA_DIAG_PUSH
200 PRAGMA_NONNULL_IGNORED
201 // Suppress false positive gcc warning, which may be an example of
202 // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=87489
203 // The warning also hasn't been seen with vanilla gcc release, so may also
204 // involve some distro-specific gcc patch.
205 ssize_t read_sz = ::read(fd, buf, buflen);
206 PRAGMA_DIAG_POP
207 ::close(fd);
208
209 // Skip if the contents is just "\n" because some machine only sets
210 // '\n' to the board name.
211 // (e.g. esys/devices/virtual/dmi/id/board_name)
212 if (read_sz > 0 && !(read_sz == 1 && *buf == '\n')) {
213 // Replace '\0' to ' '
214 for (char *ch = buf; ch < buf + read_sz - 1; ch++) {
215 if (*ch == '\0') {
216 *ch = ' ';
217 }
218 }
219 buf[read_sz - 1] = '\0';
220 return true;
221 }
222 }
223 *buf = '\0';
224 return false;
225 }
226
227 void VM_Version::get_compatible_board(char *buf, int buflen) {
228 const char *board_name_file_list[] = {
229 "/proc/device-tree/compatible",
230 "/sys/devices/virtual/dmi/id/board_name",
231 "/sys/devices/virtual/dmi/id/product_name",
232 nullptr
233 };
234
235 for (const char **fname = board_name_file_list; *fname != nullptr; fname++) {
236 if (read_fully(*fname, buf, buflen)) {
237 return;
238 }
239 }
240 }