1da07e694SWarner Losh /*
2da07e694SWarner Losh * FreeBSD sysctl() and sysarch() system call emulation
3da07e694SWarner Losh *
4da07e694SWarner Losh * Copyright (c) 2013-15 Stacey D. Son
5da07e694SWarner Losh *
6da07e694SWarner Losh * This program is free software; you can redistribute it and/or modify
7da07e694SWarner Losh * it under the terms of the GNU General Public License as published by
8da07e694SWarner Losh * the Free Software Foundation; either version 2 of the License, or
9da07e694SWarner Losh * (at your option) any later version.
10da07e694SWarner Losh *
11da07e694SWarner Losh * This program is distributed in the hope that it will be useful,
12da07e694SWarner Losh * but WITHOUT ANY WARRANTY; without even the implied warranty of
13da07e694SWarner Losh * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14da07e694SWarner Losh * GNU General Public License for more details.
15da07e694SWarner Losh *
16da07e694SWarner Losh * You should have received a copy of the GNU General Public License
17da07e694SWarner Losh * along with this program; if not, see <http://www.gnu.org/licenses/>.
18da07e694SWarner Losh */
19da07e694SWarner Losh
2048e438a3SMarkus Armbruster #include "qemu/osdep.h"
21da07e694SWarner Losh #include "qemu.h"
22da07e694SWarner Losh #include "target_arch_sysarch.h"
23da07e694SWarner Losh
2408675078SWarner Losh #include <sys/sysctl.h>
2508675078SWarner Losh
2608675078SWarner Losh /*
2708675078SWarner Losh * Length for the fixed length types.
2808675078SWarner Losh * 0 means variable length for strings and structures
2908675078SWarner Losh * Compare with sys/kern_sysctl.c ctl_size
3008675078SWarner Losh * Note: Not all types appear to be used in-tree.
3108675078SWarner Losh */
32efba70deSStacey Son static const int guest_ctl_size[CTLTYPE + 1] = {
3308675078SWarner Losh [CTLTYPE_INT] = sizeof(abi_int),
3408675078SWarner Losh [CTLTYPE_UINT] = sizeof(abi_uint),
3508675078SWarner Losh [CTLTYPE_LONG] = sizeof(abi_long),
3608675078SWarner Losh [CTLTYPE_ULONG] = sizeof(abi_ulong),
3708675078SWarner Losh [CTLTYPE_S8] = sizeof(int8_t),
3808675078SWarner Losh [CTLTYPE_S16] = sizeof(int16_t),
3908675078SWarner Losh [CTLTYPE_S32] = sizeof(int32_t),
4008675078SWarner Losh [CTLTYPE_S64] = sizeof(int64_t),
4108675078SWarner Losh [CTLTYPE_U8] = sizeof(uint8_t),
4208675078SWarner Losh [CTLTYPE_U16] = sizeof(uint16_t),
4308675078SWarner Losh [CTLTYPE_U32] = sizeof(uint32_t),
4408675078SWarner Losh [CTLTYPE_U64] = sizeof(uint64_t),
4508675078SWarner Losh };
4608675078SWarner Losh
47efba70deSStacey Son static const int host_ctl_size[CTLTYPE + 1] = {
4808675078SWarner Losh [CTLTYPE_INT] = sizeof(int),
4908675078SWarner Losh [CTLTYPE_UINT] = sizeof(u_int),
5008675078SWarner Losh [CTLTYPE_LONG] = sizeof(long),
5108675078SWarner Losh [CTLTYPE_ULONG] = sizeof(u_long),
5208675078SWarner Losh [CTLTYPE_S8] = sizeof(int8_t),
5308675078SWarner Losh [CTLTYPE_S16] = sizeof(int16_t),
5408675078SWarner Losh [CTLTYPE_S32] = sizeof(int32_t),
5508675078SWarner Losh [CTLTYPE_S64] = sizeof(int64_t),
5608675078SWarner Losh [CTLTYPE_U8] = sizeof(uint8_t),
5708675078SWarner Losh [CTLTYPE_U16] = sizeof(uint16_t),
5808675078SWarner Losh [CTLTYPE_U32] = sizeof(uint32_t),
5908675078SWarner Losh [CTLTYPE_U64] = sizeof(uint64_t),
6008675078SWarner Losh };
6108675078SWarner Losh
6208675078SWarner Losh #ifdef TARGET_ABI32
6308675078SWarner Losh /*
6408675078SWarner Losh * Limit the amount of available memory to be most of the 32-bit address
6508675078SWarner Losh * space. 0x100c000 was arrived at through trial and error as a good
6608675078SWarner Losh * definition of 'most'.
6708675078SWarner Losh */
6808675078SWarner Losh static const abi_ulong guest_max_mem = UINT32_MAX - 0x100c000 + 1;
6908675078SWarner Losh
cap_memory(uint64_t mem)70248a485bSJuergen Lock static abi_ulong cap_memory(uint64_t mem)
7108675078SWarner Losh {
7208675078SWarner Losh return MIN(guest_max_mem, mem);
7308675078SWarner Losh }
7408675078SWarner Losh #endif
7508675078SWarner Losh
scale_to_guest_pages(uint64_t pages)76248a485bSJuergen Lock static abi_ulong scale_to_guest_pages(uint64_t pages)
7708675078SWarner Losh {
7808675078SWarner Losh /* Scale pages from host to guest */
7908675078SWarner Losh pages = muldiv64(pages, qemu_real_host_page_size(), TARGET_PAGE_SIZE);
8008675078SWarner Losh #ifdef TARGET_ABI32
8108675078SWarner Losh /* cap pages if need be */
8208675078SWarner Losh pages = MIN(pages, guest_max_mem / (abi_ulong)TARGET_PAGE_SIZE);
8308675078SWarner Losh #endif
8408675078SWarner Losh return pages;
8508675078SWarner Losh }
8608675078SWarner Losh
8708675078SWarner Losh #ifdef TARGET_ABI32
8808675078SWarner Losh /* Used only for TARGET_ABI32 */
h2g_long_sat(long l)89efba70deSStacey Son static abi_long h2g_long_sat(long l)
9008675078SWarner Losh {
9108675078SWarner Losh if (l > INT32_MAX) {
9208675078SWarner Losh l = INT32_MAX;
9308675078SWarner Losh } else if (l < INT32_MIN) {
9408675078SWarner Losh l = INT32_MIN;
9508675078SWarner Losh }
9608675078SWarner Losh return l;
9708675078SWarner Losh }
9808675078SWarner Losh
h2g_ulong_sat(u_long ul)99efba70deSStacey Son static abi_ulong h2g_ulong_sat(u_long ul)
10008675078SWarner Losh {
10108675078SWarner Losh return MIN(ul, UINT32_MAX);
10208675078SWarner Losh }
10308675078SWarner Losh #endif
10408675078SWarner Losh
10508675078SWarner Losh /*
10608675078SWarner Losh * placeholder until bsd-user downstream upstreams this with its thread support
10708675078SWarner Losh */
10808675078SWarner Losh #define bsd_get_ncpu() 1
10908675078SWarner Losh
110efba70deSStacey Son /*
111efba70deSStacey Son * This uses the undocumented oidfmt interface to find the kind of a requested
112efba70deSStacey Son * sysctl, see /sys/kern/kern_sysctl.c:sysctl_sysctl_oidfmt() (compare to
113efba70deSStacey Son * src/sbin/sysctl/sysctl.c)
114efba70deSStacey Son */
oidfmt(int * oid,int len,char * fmt,uint32_t * kind)115dd7a627aSJuergen Lock static int oidfmt(int *oid, int len, char *fmt, uint32_t *kind)
116efba70deSStacey Son {
117efba70deSStacey Son int qoid[CTL_MAXNAME + 2];
118efba70deSStacey Son uint8_t buf[BUFSIZ];
119efba70deSStacey Son int i;
120efba70deSStacey Son size_t j;
121efba70deSStacey Son
122efba70deSStacey Son qoid[0] = CTL_SYSCTL;
123efba70deSStacey Son qoid[1] = CTL_SYSCTL_OIDFMT;
124efba70deSStacey Son memcpy(qoid + 2, oid, len * sizeof(int));
125efba70deSStacey Son
126efba70deSStacey Son j = sizeof(buf);
127efba70deSStacey Son i = sysctl(qoid, len + 2, buf, &j, 0, 0);
128efba70deSStacey Son if (i) {
129efba70deSStacey Son return i;
130efba70deSStacey Son }
131efba70deSStacey Son
132efba70deSStacey Son if (kind) {
133efba70deSStacey Son *kind = *(uint32_t *)buf;
134efba70deSStacey Son }
135efba70deSStacey Son
136efba70deSStacey Son if (fmt) {
137efba70deSStacey Son strcpy(fmt, (char *)(buf + sizeof(uint32_t)));
138efba70deSStacey Son }
139efba70deSStacey Son return 0;
140efba70deSStacey Son }
141efba70deSStacey Son
142efba70deSStacey Son /*
143efba70deSStacey Son * Convert the old value from host to guest.
144efba70deSStacey Son *
145efba70deSStacey Son * For LONG and ULONG on ABI32, we need to 'down convert' the 8 byte quantities
146efba70deSStacey Son * to 4 bytes. The caller setup a buffer in host memory to get this data from
147efba70deSStacey Son * the kernel and pass it to us. We do the down conversion and adjust the length
148efba70deSStacey Son * so the caller knows what to write as the returned length into the target when
149efba70deSStacey Son * it copies the down converted values into the target.
150efba70deSStacey Son *
151efba70deSStacey Son * For normal integral types, we just need to byte swap. No size changes.
152efba70deSStacey Son *
153efba70deSStacey Son * For strings and node data, there's no conversion needed.
154efba70deSStacey Son *
155efba70deSStacey Son * For opaque data, per sysctl OID converts take care of it.
156efba70deSStacey Son */
h2g_old_sysctl(void * holdp,size_t * holdlen,uint32_t kind)157dd7a627aSJuergen Lock static void h2g_old_sysctl(void *holdp, size_t *holdlen, uint32_t kind)
158efba70deSStacey Son {
159efba70deSStacey Son size_t len;
160efba70deSStacey Son int hlen, glen;
161efba70deSStacey Son uint8_t *hp, *gp;
162efba70deSStacey Son
163efba70deSStacey Son /*
164efba70deSStacey Son * Although rare, we can have arrays of sysctl. Both sysctl_old_ddb in
165efba70deSStacey Son * kern_sysctl.c and show_var in sbin/sysctl/sysctl.c have code that loops
166efba70deSStacey Son * this way. *holdlen has been set by the kernel to the host's length.
167efba70deSStacey Son * Only LONG and ULONG on ABI32 have different sizes: see below.
168efba70deSStacey Son */
169efba70deSStacey Son gp = hp = (uint8_t *)holdp;
170efba70deSStacey Son len = 0;
171efba70deSStacey Son hlen = host_ctl_size[kind & CTLTYPE];
172efba70deSStacey Son glen = guest_ctl_size[kind & CTLTYPE];
173efba70deSStacey Son
174efba70deSStacey Son /*
175efba70deSStacey Son * hlen == 0 for CTLTYPE_STRING and CTLTYPE_NODE, which need no conversion
176efba70deSStacey Son * as well as CTLTYPE_OPAQUE, which needs special converters.
177efba70deSStacey Son */
178efba70deSStacey Son if (hlen == 0) {
179efba70deSStacey Son return;
180efba70deSStacey Son }
181efba70deSStacey Son
182efba70deSStacey Son while (len < *holdlen) {
183efba70deSStacey Son if (hlen == glen) {
184efba70deSStacey Son switch (hlen) {
185efba70deSStacey Son case 1:
186efba70deSStacey Son /* Nothing needed: no byteswapping and assigning in place */
187efba70deSStacey Son break;
188efba70deSStacey Son case 2:
189efba70deSStacey Son *(uint16_t *)gp = tswap16(*(uint16_t *)hp);
190efba70deSStacey Son break;
191efba70deSStacey Son case 4:
192efba70deSStacey Son *(uint32_t *)gp = tswap32(*(uint32_t *)hp);
193efba70deSStacey Son break;
194efba70deSStacey Son case 8:
195efba70deSStacey Son *(uint64_t *)gp = tswap64(*(uint64_t *)hp);
196efba70deSStacey Son break;
197efba70deSStacey Son default:
198efba70deSStacey Son g_assert_not_reached();
199efba70deSStacey Son }
200efba70deSStacey Son } else {
201efba70deSStacey Son #ifdef TARGET_ABI32
202efba70deSStacey Son /*
203efba70deSStacey Son * Saturating assignment for the only two types that differ between
204efba70deSStacey Son * 32-bit and 64-bit machines. All other integral types have the
205efba70deSStacey Son * same, fixed size and will be converted w/o loss of precision
206efba70deSStacey Son * in the above switch.
207efba70deSStacey Son */
208efba70deSStacey Son switch (kind & CTLTYPE) {
209efba70deSStacey Son case CTLTYPE_LONG:
210efba70deSStacey Son *(abi_long *)gp = tswap32(h2g_long_sat(*(long *)hp));
211efba70deSStacey Son break;
212efba70deSStacey Son case CTLTYPE_ULONG:
213efba70deSStacey Son *(abi_ulong *)gp = tswap32(h2g_ulong_sat(*(u_long *)hp));
214efba70deSStacey Son break;
215efba70deSStacey Son default:
216efba70deSStacey Son g_assert_not_reached();
217efba70deSStacey Son }
218efba70deSStacey Son #else
219efba70deSStacey Son g_assert_not_reached();
220efba70deSStacey Son #endif
221efba70deSStacey Son }
222efba70deSStacey Son gp += glen;
223efba70deSStacey Son hp += hlen;
224efba70deSStacey Son len += hlen;
225efba70deSStacey Son }
226efba70deSStacey Son #ifdef TARGET_ABI32
227efba70deSStacey Son if (hlen != glen) {
228efba70deSStacey Son *holdlen = (*holdlen / hlen) * glen;
229efba70deSStacey Son }
230efba70deSStacey Son #endif
231efba70deSStacey Son }
232efba70deSStacey Son
2330394968aSJuergen Lock /*
2340394968aSJuergen Lock * Convert the undocmented name2oid sysctl data for the target.
2350394968aSJuergen Lock */
sysctl_name2oid(uint32_t * holdp,size_t holdlen)236dd7a627aSJuergen Lock static inline void sysctl_name2oid(uint32_t *holdp, size_t holdlen)
2370394968aSJuergen Lock {
2380394968aSJuergen Lock size_t i, num = holdlen / sizeof(uint32_t);
2390394968aSJuergen Lock
2400394968aSJuergen Lock for (i = 0; i < num; i++) {
2410394968aSJuergen Lock holdp[i] = tswap32(holdp[i]);
2420394968aSJuergen Lock }
2430394968aSJuergen Lock }
2440394968aSJuergen Lock
sysctl_oidfmt(uint32_t * holdp)245dd7a627aSJuergen Lock static inline void sysctl_oidfmt(uint32_t *holdp)
2460394968aSJuergen Lock {
2470394968aSJuergen Lock /* byte swap the kind */
2480394968aSJuergen Lock holdp[0] = tswap32(holdp[0]);
2490394968aSJuergen Lock }
2500394968aSJuergen Lock
do_freebsd_sysctl_oid(CPUArchState * env,int32_t * snamep,int32_t namelen,void * holdp,size_t * holdlenp,void * hnewp,size_t newlen)2517adda6deSKyle Evans static abi_long do_freebsd_sysctl_oid(CPUArchState *env, int32_t *snamep,
252dd7a627aSJuergen Lock int32_t namelen, void *holdp, size_t *holdlenp, void *hnewp,
253dd7a627aSJuergen Lock size_t newlen)
254dd7a627aSJuergen Lock {
255dd7a627aSJuergen Lock uint32_t kind = 0;
256dd7a627aSJuergen Lock abi_long ret;
257dd7a627aSJuergen Lock size_t holdlen, oldlen;
258dd7a627aSJuergen Lock #ifdef TARGET_ABI32
259dd7a627aSJuergen Lock void *old_holdp;
260dd7a627aSJuergen Lock #endif
261dd7a627aSJuergen Lock
262dd7a627aSJuergen Lock holdlen = oldlen = *holdlenp;
263dd7a627aSJuergen Lock oidfmt(snamep, namelen, NULL, &kind);
264dd7a627aSJuergen Lock
265dd7a627aSJuergen Lock /* Handle some arch/emulator dependent sysctl()'s here. */
266248a485bSJuergen Lock switch (snamep[0]) {
267248a485bSJuergen Lock case CTL_KERN:
268248a485bSJuergen Lock switch (snamep[1]) {
269248a485bSJuergen Lock case KERN_USRSTACK:
270248a485bSJuergen Lock if (oldlen) {
271248a485bSJuergen Lock (*(abi_ulong *)holdp) = tswapal(TARGET_USRSTACK);
272248a485bSJuergen Lock }
273248a485bSJuergen Lock holdlen = sizeof(abi_ulong);
274248a485bSJuergen Lock ret = 0;
275248a485bSJuergen Lock goto out;
276248a485bSJuergen Lock
277248a485bSJuergen Lock case KERN_PS_STRINGS:
278248a485bSJuergen Lock if (oldlen) {
279248a485bSJuergen Lock (*(abi_ulong *)holdp) = tswapal(TARGET_PS_STRINGS);
280248a485bSJuergen Lock }
281248a485bSJuergen Lock holdlen = sizeof(abi_ulong);
282248a485bSJuergen Lock ret = 0;
283248a485bSJuergen Lock goto out;
284248a485bSJuergen Lock
285248a485bSJuergen Lock default:
286248a485bSJuergen Lock break;
287248a485bSJuergen Lock }
288248a485bSJuergen Lock break;
289248a485bSJuergen Lock
290248a485bSJuergen Lock case CTL_HW:
291248a485bSJuergen Lock switch (snamep[1]) {
292248a485bSJuergen Lock case HW_MACHINE:
293248a485bSJuergen Lock holdlen = sizeof(TARGET_HW_MACHINE);
294248a485bSJuergen Lock if (holdp) {
295248a485bSJuergen Lock strlcpy(holdp, TARGET_HW_MACHINE, oldlen);
296248a485bSJuergen Lock }
297248a485bSJuergen Lock ret = 0;
298248a485bSJuergen Lock goto out;
299248a485bSJuergen Lock
300248a485bSJuergen Lock case HW_MACHINE_ARCH:
301248a485bSJuergen Lock {
302248a485bSJuergen Lock holdlen = sizeof(TARGET_HW_MACHINE_ARCH);
303248a485bSJuergen Lock if (holdp) {
304248a485bSJuergen Lock strlcpy(holdp, TARGET_HW_MACHINE_ARCH, oldlen);
305248a485bSJuergen Lock }
306248a485bSJuergen Lock ret = 0;
307248a485bSJuergen Lock goto out;
308248a485bSJuergen Lock }
309248a485bSJuergen Lock case HW_NCPU:
310248a485bSJuergen Lock if (oldlen) {
311248a485bSJuergen Lock (*(abi_int *)holdp) = tswap32(bsd_get_ncpu());
312248a485bSJuergen Lock }
313248a485bSJuergen Lock holdlen = sizeof(int32_t);
314248a485bSJuergen Lock ret = 0;
315248a485bSJuergen Lock goto out;
316248a485bSJuergen Lock #if defined(TARGET_ARM)
317248a485bSJuergen Lock case HW_FLOATINGPT:
318248a485bSJuergen Lock if (oldlen) {
319248a485bSJuergen Lock ARMCPU *cpu = env_archcpu(env);
320248a485bSJuergen Lock *(abi_int *)holdp = cpu_isar_feature(aa32_vfp, cpu);
321248a485bSJuergen Lock }
322248a485bSJuergen Lock holdlen = sizeof(abi_int);
323248a485bSJuergen Lock ret = 0;
324248a485bSJuergen Lock goto out;
325248a485bSJuergen Lock #endif
326248a485bSJuergen Lock
327248a485bSJuergen Lock
328248a485bSJuergen Lock #ifdef TARGET_ABI32
329248a485bSJuergen Lock case HW_PHYSMEM:
330248a485bSJuergen Lock case HW_USERMEM:
331248a485bSJuergen Lock case HW_REALMEM:
332248a485bSJuergen Lock holdlen = sizeof(abi_ulong);
333248a485bSJuergen Lock ret = 0;
334248a485bSJuergen Lock
335248a485bSJuergen Lock if (oldlen) {
336248a485bSJuergen Lock int mib[2] = {snamep[0], snamep[1]};
337248a485bSJuergen Lock unsigned long lvalue;
338248a485bSJuergen Lock size_t len = sizeof(lvalue);
339248a485bSJuergen Lock
340248a485bSJuergen Lock if (sysctl(mib, 2, &lvalue, &len, NULL, 0) == -1) {
341248a485bSJuergen Lock ret = -1;
342248a485bSJuergen Lock } else {
343248a485bSJuergen Lock lvalue = cap_memory(lvalue);
344248a485bSJuergen Lock (*(abi_ulong *)holdp) = tswapal((abi_ulong)lvalue);
345248a485bSJuergen Lock }
346248a485bSJuergen Lock }
347248a485bSJuergen Lock goto out;
348248a485bSJuergen Lock #endif
349248a485bSJuergen Lock
350248a485bSJuergen Lock default:
351248a485bSJuergen Lock {
352248a485bSJuergen Lock static int oid_hw_availpages;
353248a485bSJuergen Lock static int oid_hw_pagesizes;
354248a485bSJuergen Lock
355248a485bSJuergen Lock if (!oid_hw_availpages) {
356248a485bSJuergen Lock int real_oid[CTL_MAXNAME + 2];
357248a485bSJuergen Lock size_t len = sizeof(real_oid) / sizeof(int);
358248a485bSJuergen Lock
359248a485bSJuergen Lock if (sysctlnametomib("hw.availpages", real_oid, &len) >= 0) {
360248a485bSJuergen Lock oid_hw_availpages = real_oid[1];
361248a485bSJuergen Lock }
362248a485bSJuergen Lock }
363248a485bSJuergen Lock if (!oid_hw_pagesizes) {
364248a485bSJuergen Lock int real_oid[CTL_MAXNAME + 2];
365248a485bSJuergen Lock size_t len = sizeof(real_oid) / sizeof(int);
366248a485bSJuergen Lock
367248a485bSJuergen Lock if (sysctlnametomib("hw.pagesizes", real_oid, &len) >= 0) {
368248a485bSJuergen Lock oid_hw_pagesizes = real_oid[1];
369248a485bSJuergen Lock }
370248a485bSJuergen Lock }
371248a485bSJuergen Lock
372248a485bSJuergen Lock if (oid_hw_availpages && snamep[1] == oid_hw_availpages) {
373248a485bSJuergen Lock long lvalue;
374248a485bSJuergen Lock size_t len = sizeof(lvalue);
375248a485bSJuergen Lock
376248a485bSJuergen Lock if (sysctlbyname("hw.availpages", &lvalue, &len, NULL, 0) == -1) {
377248a485bSJuergen Lock ret = -1;
378248a485bSJuergen Lock } else {
379248a485bSJuergen Lock if (oldlen) {
380248a485bSJuergen Lock lvalue = scale_to_guest_pages(lvalue);
381248a485bSJuergen Lock (*(abi_ulong *)holdp) = tswapal((abi_ulong)lvalue);
382248a485bSJuergen Lock }
383248a485bSJuergen Lock holdlen = sizeof(abi_ulong);
384248a485bSJuergen Lock ret = 0;
385248a485bSJuergen Lock }
386248a485bSJuergen Lock goto out;
387248a485bSJuergen Lock }
388248a485bSJuergen Lock
389248a485bSJuergen Lock if (oid_hw_pagesizes && snamep[1] == oid_hw_pagesizes) {
390248a485bSJuergen Lock if (oldlen) {
391248a485bSJuergen Lock (*(abi_ulong *)holdp) = tswapal((abi_ulong)TARGET_PAGE_SIZE);
392248a485bSJuergen Lock ((abi_ulong *)holdp)[1] = 0;
393248a485bSJuergen Lock }
394248a485bSJuergen Lock holdlen = sizeof(abi_ulong) * 2;
395248a485bSJuergen Lock ret = 0;
396248a485bSJuergen Lock goto out;
397248a485bSJuergen Lock }
398248a485bSJuergen Lock break;
399248a485bSJuergen Lock }
400248a485bSJuergen Lock }
401248a485bSJuergen Lock break;
402248a485bSJuergen Lock
403248a485bSJuergen Lock default:
404248a485bSJuergen Lock break;
405248a485bSJuergen Lock }
406dd7a627aSJuergen Lock
407dd7a627aSJuergen Lock #ifdef TARGET_ABI32
408dd7a627aSJuergen Lock /*
409dd7a627aSJuergen Lock * For long and ulong with a 64-bit host and a 32-bit target we have to do
410dd7a627aSJuergen Lock * special things. holdlen here is the length provided by the target to the
411dd7a627aSJuergen Lock * system call. So we allocate a buffer twice as large because longs are
412dd7a627aSJuergen Lock * twice as big on the host which will be writing them. In h2g_old_sysctl
413dd7a627aSJuergen Lock * we'll adjust them and adjust the length.
414dd7a627aSJuergen Lock */
415dd7a627aSJuergen Lock if (kind == CTLTYPE_LONG || kind == CTLTYPE_ULONG) {
416dd7a627aSJuergen Lock old_holdp = holdp;
417dd7a627aSJuergen Lock holdlen = holdlen * 2;
418dd7a627aSJuergen Lock holdp = g_malloc(holdlen);
419dd7a627aSJuergen Lock }
420dd7a627aSJuergen Lock #endif
421dd7a627aSJuergen Lock
422dd7a627aSJuergen Lock ret = get_errno(sysctl(snamep, namelen, holdp, &holdlen, hnewp, newlen));
423dd7a627aSJuergen Lock if (!ret && (holdp != 0)) {
424dd7a627aSJuergen Lock
425dd7a627aSJuergen Lock if (snamep[0] == CTL_SYSCTL) {
426dd7a627aSJuergen Lock switch (snamep[1]) {
427dd7a627aSJuergen Lock case CTL_SYSCTL_NEXT:
428dd7a627aSJuergen Lock case CTL_SYSCTL_NAME2OID:
429dd7a627aSJuergen Lock case CTL_SYSCTL_NEXTNOSKIP:
430dd7a627aSJuergen Lock /*
431dd7a627aSJuergen Lock * All of these return an OID array, so we need to convert to
432dd7a627aSJuergen Lock * target.
433dd7a627aSJuergen Lock */
434dd7a627aSJuergen Lock sysctl_name2oid(holdp, holdlen);
435dd7a627aSJuergen Lock break;
436dd7a627aSJuergen Lock
437dd7a627aSJuergen Lock case CTL_SYSCTL_OIDFMT:
438dd7a627aSJuergen Lock /* Handle oidfmt */
439dd7a627aSJuergen Lock sysctl_oidfmt(holdp);
440dd7a627aSJuergen Lock break;
441dd7a627aSJuergen Lock case CTL_SYSCTL_OIDDESCR:
442dd7a627aSJuergen Lock case CTL_SYSCTL_OIDLABEL:
443dd7a627aSJuergen Lock default:
444dd7a627aSJuergen Lock /* Handle it based on the type */
445dd7a627aSJuergen Lock h2g_old_sysctl(holdp, &holdlen, kind);
446dd7a627aSJuergen Lock /* NB: None of these are LONG or ULONG */
447dd7a627aSJuergen Lock break;
448dd7a627aSJuergen Lock }
449dd7a627aSJuergen Lock } else {
450dd7a627aSJuergen Lock /*
451dd7a627aSJuergen Lock * Need to convert from host to target. All the weird special cases
452dd7a627aSJuergen Lock * are handled above.
453dd7a627aSJuergen Lock */
454dd7a627aSJuergen Lock h2g_old_sysctl(holdp, &holdlen, kind);
455dd7a627aSJuergen Lock #ifdef TARGET_ABI32
456dd7a627aSJuergen Lock /*
457dd7a627aSJuergen Lock * For the 32-bit on 64-bit case, for longs we need to copy the
458dd7a627aSJuergen Lock * now-converted buffer to the target and free the buffer.
459dd7a627aSJuergen Lock */
460dd7a627aSJuergen Lock if (kind == CTLTYPE_LONG || kind == CTLTYPE_ULONG) {
461dd7a627aSJuergen Lock memcpy(old_holdp, holdp, holdlen);
462dd7a627aSJuergen Lock g_free(holdp);
463dd7a627aSJuergen Lock holdp = old_holdp;
464dd7a627aSJuergen Lock }
465dd7a627aSJuergen Lock #endif
466dd7a627aSJuergen Lock }
467dd7a627aSJuergen Lock }
468dd7a627aSJuergen Lock
469248a485bSJuergen Lock out:
470dd7a627aSJuergen Lock *holdlenp = holdlen;
471dd7a627aSJuergen Lock return ret;
472dd7a627aSJuergen Lock }
473dd7a627aSJuergen Lock
474*6da777e2SKyle Evans /*
475*6da777e2SKyle Evans * This syscall was created to make sysctlbyname(3) more efficient, but we can't
476*6da777e2SKyle Evans * really provide it in bsd-user. Notably, we must always translate the names
477*6da777e2SKyle Evans * independently since some sysctl values have to be faked for the target
478*6da777e2SKyle Evans * environment, so it still has to break down to two syscalls for the underlying
479*6da777e2SKyle Evans * implementation.
480*6da777e2SKyle Evans */
do_freebsd_sysctlbyname(CPUArchState * env,abi_ulong namep,int32_t namelen,abi_ulong oldp,abi_ulong oldlenp,abi_ulong newp,abi_ulong newlen)481*6da777e2SKyle Evans abi_long do_freebsd_sysctlbyname(CPUArchState *env, abi_ulong namep,
482*6da777e2SKyle Evans int32_t namelen, abi_ulong oldp, abi_ulong oldlenp, abi_ulong newp,
483*6da777e2SKyle Evans abi_ulong newlen)
484*6da777e2SKyle Evans {
485*6da777e2SKyle Evans abi_long ret = -TARGET_EFAULT;
486*6da777e2SKyle Evans void *holdp = NULL, *hnewp = NULL;
487*6da777e2SKyle Evans char *snamep = NULL;
488*6da777e2SKyle Evans int oid[CTL_MAXNAME + 2];
489*6da777e2SKyle Evans size_t holdlen, oidplen;
490*6da777e2SKyle Evans abi_ulong oldlen = 0;
491*6da777e2SKyle Evans
492*6da777e2SKyle Evans /* oldlenp is read/write, pre-check here for write */
493*6da777e2SKyle Evans if (oldlenp) {
494*6da777e2SKyle Evans if (!access_ok(VERIFY_WRITE, oldlenp, sizeof(abi_ulong)) ||
495*6da777e2SKyle Evans get_user_ual(oldlen, oldlenp)) {
496*6da777e2SKyle Evans goto out;
497*6da777e2SKyle Evans }
498*6da777e2SKyle Evans }
499*6da777e2SKyle Evans snamep = lock_user_string(namep);
500*6da777e2SKyle Evans if (snamep == NULL) {
501*6da777e2SKyle Evans goto out;
502*6da777e2SKyle Evans }
503*6da777e2SKyle Evans if (newp) {
504*6da777e2SKyle Evans hnewp = lock_user(VERIFY_READ, newp, newlen, 1);
505*6da777e2SKyle Evans if (hnewp == NULL) {
506*6da777e2SKyle Evans goto out;
507*6da777e2SKyle Evans }
508*6da777e2SKyle Evans }
509*6da777e2SKyle Evans if (oldp) {
510*6da777e2SKyle Evans holdp = lock_user(VERIFY_WRITE, oldp, oldlen, 0);
511*6da777e2SKyle Evans if (holdp == NULL) {
512*6da777e2SKyle Evans goto out;
513*6da777e2SKyle Evans }
514*6da777e2SKyle Evans }
515*6da777e2SKyle Evans holdlen = oldlen;
516*6da777e2SKyle Evans
517*6da777e2SKyle Evans oidplen = ARRAY_SIZE(oid);
518*6da777e2SKyle Evans if (sysctlnametomib(snamep, oid, &oidplen) != 0) {
519*6da777e2SKyle Evans ret = -TARGET_EINVAL;
520*6da777e2SKyle Evans goto out;
521*6da777e2SKyle Evans }
522*6da777e2SKyle Evans
523*6da777e2SKyle Evans ret = do_freebsd_sysctl_oid(env, oid, oidplen, holdp, &holdlen, hnewp,
524*6da777e2SKyle Evans newlen);
525*6da777e2SKyle Evans
526*6da777e2SKyle Evans /*
527*6da777e2SKyle Evans * writeability pre-checked above. __sysctl(2) returns ENOMEM and updates
528*6da777e2SKyle Evans * oldlenp for the proper size to use.
529*6da777e2SKyle Evans */
530*6da777e2SKyle Evans if (oldlenp && (ret == 0 || ret == -TARGET_ENOMEM)) {
531*6da777e2SKyle Evans put_user_ual(holdlen, oldlenp);
532*6da777e2SKyle Evans }
533*6da777e2SKyle Evans out:
534*6da777e2SKyle Evans unlock_user(snamep, namep, 0);
535*6da777e2SKyle Evans unlock_user(holdp, oldp, ret == 0 ? holdlen : 0);
536*6da777e2SKyle Evans unlock_user(hnewp, newp, 0);
537*6da777e2SKyle Evans
538*6da777e2SKyle Evans return ret;
539*6da777e2SKyle Evans }
540*6da777e2SKyle Evans
do_freebsd_sysctl(CPUArchState * env,abi_ulong namep,int32_t namelen,abi_ulong oldp,abi_ulong oldlenp,abi_ulong newp,abi_ulong newlen)5417adda6deSKyle Evans abi_long do_freebsd_sysctl(CPUArchState *env, abi_ulong namep, int32_t namelen,
5427adda6deSKyle Evans abi_ulong oldp, abi_ulong oldlenp, abi_ulong newp, abi_ulong newlen)
5437adda6deSKyle Evans {
5447adda6deSKyle Evans abi_long ret = -TARGET_EFAULT;
5457adda6deSKyle Evans void *hnamep, *holdp = NULL, *hnewp = NULL;
5467adda6deSKyle Evans size_t holdlen;
5477adda6deSKyle Evans abi_ulong oldlen = 0;
5487adda6deSKyle Evans int32_t *snamep = g_malloc(sizeof(int32_t) * namelen), *p, *q, i;
5497adda6deSKyle Evans
5507adda6deSKyle Evans /* oldlenp is read/write, pre-check here for write */
5517adda6deSKyle Evans if (oldlenp) {
5527adda6deSKyle Evans if (!access_ok(VERIFY_WRITE, oldlenp, sizeof(abi_ulong)) ||
5537adda6deSKyle Evans get_user_ual(oldlen, oldlenp)) {
5547adda6deSKyle Evans goto out;
5557adda6deSKyle Evans }
5567adda6deSKyle Evans }
5577adda6deSKyle Evans hnamep = lock_user(VERIFY_READ, namep, namelen, 1);
5587adda6deSKyle Evans if (hnamep == NULL) {
5597adda6deSKyle Evans goto out;
5607adda6deSKyle Evans }
5617adda6deSKyle Evans if (newp) {
5627adda6deSKyle Evans hnewp = lock_user(VERIFY_READ, newp, newlen, 1);
5637adda6deSKyle Evans if (hnewp == NULL) {
5647adda6deSKyle Evans goto out;
5657adda6deSKyle Evans }
5667adda6deSKyle Evans }
5677adda6deSKyle Evans if (oldp) {
5687adda6deSKyle Evans holdp = lock_user(VERIFY_WRITE, oldp, oldlen, 0);
5697adda6deSKyle Evans if (holdp == NULL) {
5707adda6deSKyle Evans goto out;
5717adda6deSKyle Evans }
5727adda6deSKyle Evans }
5737adda6deSKyle Evans holdlen = oldlen;
5747adda6deSKyle Evans for (p = hnamep, q = snamep, i = 0; i < namelen; p++, i++, q++) {
5757adda6deSKyle Evans *q = tswap32(*p);
5767adda6deSKyle Evans }
5777adda6deSKyle Evans
5787adda6deSKyle Evans ret = do_freebsd_sysctl_oid(env, snamep, namelen, holdp, &holdlen, hnewp,
5797adda6deSKyle Evans newlen);
5807adda6deSKyle Evans
5817adda6deSKyle Evans /*
5827adda6deSKyle Evans * writeability pre-checked above. __sysctl(2) returns ENOMEM and updates
5837adda6deSKyle Evans * oldlenp for the proper size to use.
5847adda6deSKyle Evans */
5857adda6deSKyle Evans if (oldlenp && (ret == 0 || ret == -TARGET_ENOMEM)) {
5867adda6deSKyle Evans put_user_ual(holdlen, oldlenp);
5877adda6deSKyle Evans }
5887adda6deSKyle Evans unlock_user(hnamep, namep, 0);
5897adda6deSKyle Evans unlock_user(holdp, oldp, ret == 0 ? holdlen : 0);
5907adda6deSKyle Evans out:
5917adda6deSKyle Evans g_free(snamep);
5927adda6deSKyle Evans return ret;
5937adda6deSKyle Evans }
5947adda6deSKyle Evans
595da07e694SWarner Losh /* sysarch() is architecture dependent. */
do_freebsd_sysarch(void * cpu_env,abi_long arg1,abi_long arg2)596da07e694SWarner Losh abi_long do_freebsd_sysarch(void *cpu_env, abi_long arg1, abi_long arg2)
597da07e694SWarner Losh {
598da07e694SWarner Losh return do_freebsd_arch_sysarch(cpu_env, arg1, arg2);
599da07e694SWarner Losh }
600