1 /*
2  * virarch.c: architecture handling
3  *
4  * Copyright (C) 2012 Red Hat, Inc.
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library.  If not, see
18  * <http://www.gnu.org/licenses/>.
19  *
20  */
21 
22 #include <config.h>
23 
24 #ifdef WIN32
25 # define WIN32_LEAN_AND_MEAN
26 # include <windows.h>
27 #else
28 # include <sys/utsname.h>
29 #endif
30 
31 #include "virlog.h"
32 #include "virarch.h"
33 
34 VIR_LOG_INIT("util.arch");
35 
36 /* The canonical names are used in XML documents. ie ABI sensitive */
37 static const struct virArchData {
38     const char *name;
39     unsigned int wordsize;
40     virArchEndian endian;
41 } virArchData[] = {
42     { "none",          0, VIR_ARCH_LITTLE_ENDIAN },
43     { "alpha",        64, VIR_ARCH_BIG_ENDIAN },
44     { "armv6l",       32, VIR_ARCH_LITTLE_ENDIAN },
45     { "armv7l",       32, VIR_ARCH_LITTLE_ENDIAN },
46     { "armv7b",       32, VIR_ARCH_BIG_ENDIAN },
47 
48     { "aarch64",      64, VIR_ARCH_LITTLE_ENDIAN },
49     { "cris",         32, VIR_ARCH_LITTLE_ENDIAN },
50     { "i686",         32, VIR_ARCH_LITTLE_ENDIAN },
51     { "ia64",         64, VIR_ARCH_LITTLE_ENDIAN },
52     { "lm32",         32, VIR_ARCH_BIG_ENDIAN },
53 
54     { "m68k",         32, VIR_ARCH_BIG_ENDIAN },
55     { "microblaze",   32, VIR_ARCH_BIG_ENDIAN },
56     { "microblazeel", 32, VIR_ARCH_LITTLE_ENDIAN},
57     { "mips",         32, VIR_ARCH_BIG_ENDIAN },
58     { "mipsel",       32, VIR_ARCH_LITTLE_ENDIAN },
59 
60     { "mips64",       64, VIR_ARCH_BIG_ENDIAN },
61     { "mips64el",     64, VIR_ARCH_LITTLE_ENDIAN },
62     { "openrisc",     32, VIR_ARCH_BIG_ENDIAN },
63     { "parisc",       32, VIR_ARCH_BIG_ENDIAN },
64     { "parisc64",     64, VIR_ARCH_BIG_ENDIAN },
65 
66     { "ppc",          32, VIR_ARCH_BIG_ENDIAN },
67     { "ppcle",        32, VIR_ARCH_LITTLE_ENDIAN },
68     { "ppc64",        64, VIR_ARCH_BIG_ENDIAN },
69     { "ppc64le",      64, VIR_ARCH_LITTLE_ENDIAN },
70     { "ppcemb",       32, VIR_ARCH_BIG_ENDIAN },
71 
72     { "riscv32",      32, VIR_ARCH_LITTLE_ENDIAN },
73     { "riscv64",      64, VIR_ARCH_LITTLE_ENDIAN },
74     { "s390",         32, VIR_ARCH_BIG_ENDIAN },
75     { "s390x",        64, VIR_ARCH_BIG_ENDIAN },
76     { "sh4",          32, VIR_ARCH_LITTLE_ENDIAN },
77 
78     { "sh4eb",        64, VIR_ARCH_BIG_ENDIAN },
79     { "sparc",        32, VIR_ARCH_BIG_ENDIAN },
80     { "sparc64",      64, VIR_ARCH_BIG_ENDIAN },
81     { "unicore32",    32, VIR_ARCH_LITTLE_ENDIAN },
82     { "x86_64",       64, VIR_ARCH_LITTLE_ENDIAN },
83 
84     { "xtensa",       32, VIR_ARCH_LITTLE_ENDIAN },
85     { "xtensaeb",     32, VIR_ARCH_BIG_ENDIAN },
86 };
87 
88 G_STATIC_ASSERT(G_N_ELEMENTS(virArchData) == VIR_ARCH_LAST);
89 
90 
91 /**
92  * virArchGetWordSize:
93  * @arch: the CPU architecture
94  *
95  * Return the wordsize of the CPU architecture (32 or 64)
96  */
virArchGetWordSize(virArch arch)97 unsigned int virArchGetWordSize(virArch arch)
98 {
99     if (arch >= VIR_ARCH_LAST)
100         arch = VIR_ARCH_NONE;
101 
102     return virArchData[arch].wordsize;
103 }
104 
105 /**
106  * virArchGetEndian:
107  * @arch: the CPU architecture
108  *
109  * Return the endian-ness of the CPU architecture
110  * (VIR_ARCH_LITTLE_ENDIAN or VIR_ARCH_BIG_ENDIAN)
111  */
virArchGetEndian(virArch arch)112 virArchEndian virArchGetEndian(virArch arch)
113 {
114     if (arch >= VIR_ARCH_LAST)
115         arch = VIR_ARCH_NONE;
116 
117     return virArchData[arch].endian;
118 }
119 
120 /**
121  * virArchToString:
122  * @arch: the CPU architecture
123  *
124  * Return the string name of the architecture
125  */
virArchToString(virArch arch)126 const char *virArchToString(virArch arch)
127 {
128     if (arch >= VIR_ARCH_LAST)
129         arch = VIR_ARCH_NONE;
130 
131     return virArchData[arch].name;
132 }
133 
134 
135 /**
136  * virArchFromString:
137  * @archstr: the CPU architecture string
138  *
139  * Return the architecture matching @archstr,
140  * defaulting to VIR_ARCH_NONE if unidentified
141  */
virArchFromString(const char * archstr)142 virArch virArchFromString(const char *archstr)
143 {
144     size_t i;
145     for (i = 1; i < VIR_ARCH_LAST; i++) {
146         if (STREQ(virArchData[i].name, archstr))
147             return i;
148     }
149 
150     VIR_DEBUG("Unknown arch %s", archstr);
151     return VIR_ARCH_NONE;
152 }
153 
154 
155 /**
156  * virArchFromHost:
157  *
158  * Return the host architecture. Prefer this to the
159  * uname 'machine' field, since this will canonicalize
160  * architecture names like 'amd64' into 'x86_64'.
161  */
162 #ifdef WIN32
163 
164 /*
165  * Missing in ming64 headers 6.0.0, but defined as '12' in:
166  *
167  * https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/ns-sysinfoapi-system_info
168  */
169 # ifndef PROCESSOR_ARCHITECTURE_ARM64
170 #  define PROCESSOR_ARCHITECTURE_ARM64 12
171 # endif
172 
virArchFromHost(void)173 virArch virArchFromHost(void)
174 {
175     SYSTEM_INFO info;
176 
177     GetSystemInfo(&info);
178 
179     switch (info.wProcessorArchitecture) {
180     case PROCESSOR_ARCHITECTURE_AMD64:
181         return VIR_ARCH_X86_64;
182     case PROCESSOR_ARCHITECTURE_IA64:
183         return VIR_ARCH_ITANIUM;
184     case PROCESSOR_ARCHITECTURE_INTEL:
185     case PROCESSOR_ARCHITECTURE_IA32_ON_WIN64:
186         return VIR_ARCH_I686;
187     case PROCESSOR_ARCHITECTURE_MIPS:
188         return VIR_ARCH_MIPS;
189     case PROCESSOR_ARCHITECTURE_ALPHA:
190         return VIR_ARCH_ALPHA;
191     case PROCESSOR_ARCHITECTURE_PPC:
192         return VIR_ARCH_PPC;
193     case PROCESSOR_ARCHITECTURE_SHX:
194         return VIR_ARCH_SH4;
195     case PROCESSOR_ARCHITECTURE_ARM:
196         return VIR_ARCH_ARMV7L;
197     case PROCESSOR_ARCHITECTURE_ARM64:
198         return VIR_ARCH_AARCH64;
199     default:
200         VIR_WARN("Unknown host arch '%d', report to libvir-list@redhat.com",
201                  info.wProcessorArchitecture);
202         return VIR_ARCH_NONE;
203     }
204 }
205 #else /* !WIN32 */
virArchFromHost(void)206 virArch virArchFromHost(void)
207 {
208     struct utsname ut;
209     virArch arch;
210 
211     uname(&ut);
212 
213     /* Some special cases we need to handle first
214      * for non-canonical names */
215     if (strlen(ut.machine) == 4 &&
216         ut.machine[0] == 'i' &&
217         ut.machine[2] == '8' &&
218         ut.machine[3] == '6' &&
219         ut.machine[4] == '\0') {
220         arch = VIR_ARCH_I686;
221     } else if (STREQ(ut.machine, "amd64")) {
222         arch = VIR_ARCH_X86_64;
223     } else if (STREQ(ut.machine, "arm64")) {
224         arch = VIR_ARCH_AARCH64;
225     } else {
226         /* Otherwise assume the canonical name */
227         if ((arch = virArchFromString(ut.machine)) == VIR_ARCH_NONE) {
228             VIR_WARN("Unknown host arch %s, report to libvir-list@redhat.com",
229                      ut.machine);
230         }
231     }
232 
233     VIR_DEBUG("Mapped %s to %d (%s)",
234               ut.machine, arch, virArchToString(arch));
235 
236     return arch;
237 }
238 #endif /* !WIN32 */
239