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 bool isAtLeastKernelVersion(unsigned major, unsigned minor, unsigned sub) {
46     return kernelVersion() >= KVER(major, minor, sub);
47 }
48 
isKernelVersion(unsigned major,unsigned minor)49 static inline bool isKernelVersion(unsigned major, unsigned minor) {
50     return isAtLeastKernelVersion(major, minor, 0) && !isAtLeastKernelVersion(major, minor + 1, 0);
51 }
52 
isLtsKernel()53 static inline bool __unused isLtsKernel() {
54     return isKernelVersion(4,  4) ||  // minimum for Android R
55            isKernelVersion(4,  9) ||  // minimum for Android S & T
56            isKernelVersion(4, 14) ||  // minimum for Android U
57            isKernelVersion(4, 19) ||  // minimum for Android V
58            isKernelVersion(5,  4) ||  // first supported in Android R
59            isKernelVersion(5, 10) ||  // first supported in Android S
60            isKernelVersion(5, 15) ||  // first supported in Android T
61            isKernelVersion(6,  1) ||  // first supported in Android U
62            isKernelVersion(6,  6);    // first supported in Android V
63 }
64 
65 // Figure out the bitness of userspace.
66 // Trivial and known at compile time.
isUserspace32bit()67 static constexpr bool isUserspace32bit() {
68     return sizeof(void*) == 4;
69 }
70 
isUserspace64bit()71 static constexpr bool isUserspace64bit() {
72     return sizeof(void*) == 8;
73 }
74 
75 #if defined(__LP64__)
76 static_assert(isUserspace64bit(), "huh? LP64 must have 64-bit userspace");
77 #elif defined(__ILP32__)
78 static_assert(isUserspace32bit(), "huh? ILP32 must have 32-bit userspace");
79 #else
80 #error "huh? must be either LP64 (64-bit userspace) or ILP32 (32-bit userspace)"
81 #endif
82 
83 static_assert(isUserspace32bit() || isUserspace64bit(), "must be either 32 or 64 bit");
84 
85 // Figure out the bitness of the kernel.
isKernel64Bit()86 static inline bool isKernel64Bit() {
87     // a 64-bit userspace requires a 64-bit kernel
88     if (isUserspace64bit()) return true;
89 
90     static bool init = false;
91     static bool cache = false;
92     if (init) return cache;
93 
94     // Retrieve current personality - on Linux this system call *cannot* fail.
95     int p = personality(0xffffffff);
96     // But if it does just assume kernel and userspace (which is 32-bit) match...
97     if (p == -1) return false;
98 
99     // This will effectively mask out the bottom 8 bits, and switch to 'native'
100     // personality, and then return the previous personality of this thread
101     // (likely PER_LINUX or PER_LINUX32) with any extra options unmodified.
102     int q = personality((p & ~PER_MASK) | PER_LINUX);
103     // Per man page this theoretically could error out with EINVAL,
104     // but kernel code analysis suggests setting PER_LINUX cannot fail.
105     // Either way, assume kernel and userspace (which is 32-bit) match...
106     if (q != p) return false;
107 
108     struct utsname u;
109     (void)uname(&u);  // only possible failure is EFAULT, but u is on stack.
110 
111     // Switch back to previous personality.
112     // Theoretically could fail with EINVAL on arm64 with no 32-bit support,
113     // but then we wouldn't have fetched 'p' from the kernel in the first place.
114     // Either way there's nothing meaningful we can do in case of error.
115     // Since PER_LINUX32 vs PER_LINUX only affects uname.machine it doesn't
116     // really hurt us either.  We're really just switching back to be 'clean'.
117     (void)personality(p);
118 
119     // Possible values of utsname.machine observed on x86_64 desktop (arm via qemu):
120     //   x86_64 i686 aarch64 armv7l
121     // additionally observed on arm device:
122     //   armv8l
123     // presumably also might just be possible:
124     //   i386 i486 i586
125     // and there might be other weird arm32 cases.
126     // We note that the 64 is present in both 64-bit archs,
127     // and in general is likely to be present in only 64-bit archs.
128     cache = !!strstr(u.machine, "64");
129     init = true;
130     return cache;
131 }
132 
isKernel32Bit()133 static inline __unused bool isKernel32Bit() {
134     return !isKernel64Bit();
135 }
136 
isArm()137 static constexpr bool isArm() {
138 #if defined(__arm__)
139     static_assert(isUserspace32bit(), "huh? arm must be 32 bit");
140     return true;
141 #elif defined(__aarch64__)
142     static_assert(isUserspace64bit(), "aarch64 must be LP64 - no support for ILP32");
143     return true;
144 #else
145     return false;
146 #endif
147 }
148 
isX86()149 static constexpr bool isX86() {
150 #if defined(__i386__)
151     static_assert(isUserspace32bit(), "huh? i386 must be 32 bit");
152     return true;
153 #elif defined(__x86_64__)
154     static_assert(isUserspace64bit(), "x86_64 must be LP64 - no support for ILP32 (x32)");
155     return true;
156 #else
157     return false;
158 #endif
159 }
160 
isRiscV()161 static constexpr bool isRiscV() {
162 #if defined(__riscv)
163     static_assert(isUserspace64bit(), "riscv must be 64 bit");
164     return true;
165 #else
166     return false;
167 #endif
168 }
169 
170 static_assert(isArm() || isX86() || isRiscV(), "Unknown architecture");
171 
describeArch()172 static __unused const char * describeArch() {
173     // ordered so as to make it easier to compile time optimize,
174     // only thing not known at compile time is isKernel64Bit()
175     if (isUserspace64bit()) {
176         if (isArm()) return "64-on-aarch64";
177         if (isX86()) return "64-on-x86-64";
178         if (isRiscV()) return "64-on-riscv64";
179     } else if (isKernel64Bit()) {
180         if (isArm()) return "32-on-aarch64";
181         if (isX86()) return "32-on-x86-64";
182     } else {
183         if (isArm()) return "32-on-arm32";
184         if (isX86()) return "32-on-x86-32";
185     }
186 }
187 
188 }  // namespace bpf
189 }  // namespace android
190