xref: /openbmc/qemu/bsd-user/freebsd/os-sys.c (revision 6da777e2)
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