1 /*
2 * Copyright (C) 2022 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #pragma once
18
19 #include <stdio.h>
20 #include <string.h>
21 #include <sys/personality.h>
22 #include <sys/utsname.h>
23
24 namespace android {
25 namespace bpf {
26
27 #define KVER(a, b, c) (((a) << 24) + ((b) << 16) + (c))
28
uncachedKernelVersion()29 static inline unsigned uncachedKernelVersion() {
30 struct utsname buf;
31 if (uname(&buf)) return 0;
32
33 unsigned kver_major = 0;
34 unsigned kver_minor = 0;
35 unsigned kver_sub = 0;
36 (void)sscanf(buf.release, "%u.%u.%u", &kver_major, &kver_minor, &kver_sub);
37 return KVER(kver_major, kver_minor, kver_sub);
38 }
39
kernelVersion()40 static inline unsigned kernelVersion() {
41 static unsigned kver = uncachedKernelVersion();
42 return kver;
43 }
44
isAtLeastKernelVersion(unsigned major,unsigned minor,unsigned sub)45 static inline __unused bool isAtLeastKernelVersion(unsigned major, unsigned minor, unsigned sub) {
46 return kernelVersion() >= KVER(major, minor, sub);
47 }
48
49 // Figure out the bitness of userspace.
50 // Trivial and known at compile time.
isUserspace32bit()51 static constexpr bool isUserspace32bit() {
52 return sizeof(void*) == 4;
53 }
54
isUserspace64bit()55 static constexpr bool isUserspace64bit() {
56 return sizeof(void*) == 8;
57 }
58
59 #if defined(__LP64__)
60 static_assert(isUserspace64bit(), "huh?");
61 #elif defined(__ILP32__)
62 static_assert(isUserspace32bit(), "huh?");
63 #else
64 #error "huh?"
65 #endif
66
67 static_assert(isUserspace32bit() || isUserspace64bit(), "must be either 32 or 64 bit");
68
69 // Figure out the bitness of the kernel.
isKernel64Bit()70 static inline bool isKernel64Bit() {
71 // a 64-bit userspace requires a 64-bit kernel
72 if (isUserspace64bit()) return true;
73
74 static bool init = false;
75 static bool cache = false;
76 if (init) return cache;
77
78 // Retrieve current personality - on Linux this system call *cannot* fail.
79 int p = personality(0xffffffff);
80 // But if it does just assume kernel and userspace (which is 32-bit) match...
81 if (p == -1) return false;
82
83 // This will effectively mask out the bottom 8 bits, and switch to 'native'
84 // personality, and then return the previous personality of this thread
85 // (likely PER_LINUX or PER_LINUX32) with any extra options unmodified.
86 int q = personality((p & ~PER_MASK) | PER_LINUX);
87 // Per man page this theoretically could error out with EINVAL,
88 // but kernel code analysis suggests setting PER_LINUX cannot fail.
89 // Either way, assume kernel and userspace (which is 32-bit) match...
90 if (q != p) return false;
91
92 struct utsname u;
93 (void)uname(&u); // only possible failure is EFAULT, but u is on stack.
94
95 // Switch back to previous personality.
96 // Theoretically could fail with EINVAL on arm64 with no 32-bit support,
97 // but then we wouldn't have fetched 'p' from the kernel in the first place.
98 // Either way there's nothing meaningful we can do in case of error.
99 // Since PER_LINUX32 vs PER_LINUX only affects uname.machine it doesn't
100 // really hurt us either. We're really just switching back to be 'clean'.
101 (void)personality(p);
102
103 // Possible values of utsname.machine observed on x86_64 desktop (arm via qemu):
104 // x86_64 i686 aarch64 armv7l
105 // additionally observed on arm device:
106 // armv8l
107 // presumably also might just be possible:
108 // i386 i486 i586
109 // and there might be other weird arm32 cases.
110 // We note that the 64 is present in both 64-bit archs,
111 // and in general is likely to be present in only 64-bit archs.
112 cache = !!strstr(u.machine, "64");
113 init = true;
114 return cache;
115 }
116
isKernel32Bit()117 static inline __unused bool isKernel32Bit() {
118 return !isKernel64Bit();
119 }
120
isArm()121 static constexpr bool isArm() {
122 #if defined(__arm__) || defined(__aarch64__)
123 return true;
124 #else
125 return false;
126 #endif
127 }
128
isX86()129 static constexpr bool isX86() {
130 #if defined(__i386__) || defined(__x86_64__)
131 return true;
132 #else
133 return false;
134 #endif
135 }
136
isRiscV()137 static constexpr bool isRiscV() {
138 #if defined(__riscv)
139 static_assert(isUserspace64bit(), "riscv must be 64 bit");
140 return true;
141 #else
142 return false;
143 #endif
144 }
145
146 static_assert(isArm() || isX86() || isRiscV(), "Unknown architecture");
147
describeArch()148 static __unused const char * describeArch() {
149 // ordered so as to make it easier to compile time optimize,
150 // only thing not known at compile time is isKernel64Bit()
151 if (isUserspace64bit()) {
152 if (isArm()) return "64-on-aarch64";
153 if (isX86()) return "64-on-x86-64";
154 if (isRiscV()) return "64-on-riscv64";
155 } else if (isKernel64Bit()) {
156 if (isArm()) return "32-on-aarch64";
157 if (isX86()) return "32-on-x86-64";
158 } else {
159 if (isArm()) return "32-on-arm32";
160 if (isX86()) return "32-on-x86-32";
161 }
162 }
163
164 } // namespace bpf
165 } // namespace android
166