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 }