seg_mf.c revision 7756:d653ef0c7180
1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22/*
23 * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 */
26
27/*
28 * Machine frame segment driver.  This segment driver allows dom0 processes to
29 * map pages of other domains or Xen (e.g. during save/restore).  ioctl()s on
30 * the privcmd driver provide the MFN values backing each mapping, and we map
31 * them into the process's address space at this time.  Demand-faulting is not
32 * supported by this driver due to the requirements upon some of the ioctl()s.
33 */
34
35
36#include <sys/types.h>
37#include <sys/systm.h>
38#include <sys/vmsystm.h>
39#include <sys/mman.h>
40#include <sys/errno.h>
41#include <sys/kmem.h>
42#include <sys/cmn_err.h>
43#include <sys/vnode.h>
44#include <sys/conf.h>
45#include <sys/debug.h>
46#include <sys/lgrp.h>
47#include <sys/hypervisor.h>
48
49#include <vm/page.h>
50#include <vm/hat.h>
51#include <vm/as.h>
52#include <vm/seg.h>
53
54#include <vm/hat_pte.h>
55#include <vm/hat_i86.h>
56#include <vm/seg_mf.h>
57
58#include <sys/fs/snode.h>
59
60#define	VTOCVP(vp)	(VTOS(vp)->s_commonvp)
61
62typedef struct segmf_mfn_s {
63	mfn_t		m_mfn;
64} segmf_mfn_t;
65
66/* g_flags */
67#define	SEGMF_GFLAGS_WR		0x1
68#define	SEGMF_GFLAGS_MAPPED	0x2
69typedef struct segmf_gref_s {
70	uint64_t	g_ptep;
71	grant_ref_t	g_gref;
72	uint32_t	g_flags;
73	grant_handle_t	g_handle;
74} segmf_gref_t;
75
76typedef union segmf_mu_u {
77	segmf_mfn_t	m;
78	segmf_gref_t	g;
79} segmf_mu_t;
80
81typedef enum {
82	SEGMF_MAP_EMPTY = 0,
83	SEGMF_MAP_MFN,
84	SEGMF_MAP_GREF
85} segmf_map_type_t;
86
87typedef struct segmf_map_s {
88	segmf_map_type_t	t_type;
89	segmf_mu_t		u;
90} segmf_map_t;
91
92struct segmf_data {
93	kmutex_t	lock;
94	struct vnode	*vp;
95	uchar_t		prot;
96	uchar_t		maxprot;
97	size_t		softlockcnt;
98	domid_t		domid;
99	segmf_map_t	*map;
100};
101
102static struct seg_ops segmf_ops;
103
104static int segmf_fault_gref_range(struct seg *seg, caddr_t addr, size_t len);
105
106static struct segmf_data *
107segmf_data_zalloc(struct seg *seg)
108{
109	struct segmf_data *data = kmem_zalloc(sizeof (*data), KM_SLEEP);
110
111	mutex_init(&data->lock, "segmf.lock", MUTEX_DEFAULT, NULL);
112	seg->s_ops = &segmf_ops;
113	seg->s_data = data;
114	return (data);
115}
116
117int
118segmf_create(struct seg *seg, void *args)
119{
120	struct segmf_crargs *a = args;
121	struct segmf_data *data;
122	struct as *as = seg->s_as;
123	pgcnt_t i, npages = seg_pages(seg);
124	int error;
125
126	hat_map(as->a_hat, seg->s_base, seg->s_size, HAT_MAP);
127
128	data = segmf_data_zalloc(seg);
129	data->vp = specfind(a->dev, VCHR);
130	data->prot = a->prot;
131	data->maxprot = a->maxprot;
132
133	data->map = kmem_alloc(npages * sizeof (segmf_map_t), KM_SLEEP);
134	for (i = 0; i < npages; i++) {
135		data->map[i].t_type = SEGMF_MAP_EMPTY;
136	}
137
138	error = VOP_ADDMAP(VTOCVP(data->vp), 0, as, seg->s_base, seg->s_size,
139	    data->prot, data->maxprot, MAP_SHARED, CRED(), NULL);
140
141	if (error != 0)
142		hat_unload(as->a_hat,
143		    seg->s_base, seg->s_size, HAT_UNLOAD_UNMAP);
144	return (error);
145}
146
147/*
148 * Duplicate a seg and return new segment in newseg.
149 */
150static int
151segmf_dup(struct seg *seg, struct seg *newseg)
152{
153	struct segmf_data *data = seg->s_data;
154	struct segmf_data *ndata;
155	pgcnt_t npages = seg_pages(newseg);
156	size_t sz;
157
158	ndata = segmf_data_zalloc(newseg);
159
160	VN_HOLD(data->vp);
161	ndata->vp = data->vp;
162	ndata->prot = data->prot;
163	ndata->maxprot = data->maxprot;
164	ndata->domid = data->domid;
165
166	sz = npages * sizeof (segmf_map_t);
167	ndata->map = kmem_alloc(sz, KM_SLEEP);
168	bcopy(data->map, ndata->map, sz);
169
170	return (VOP_ADDMAP(VTOCVP(ndata->vp), 0, newseg->s_as,
171	    newseg->s_base, newseg->s_size, ndata->prot, ndata->maxprot,
172	    MAP_SHARED, CRED(), NULL));
173}
174
175/*
176 * We only support unmapping the whole segment, and we automatically unlock
177 * what we previously soft-locked.
178 */
179static int
180segmf_unmap(struct seg *seg, caddr_t addr, size_t len)
181{
182	struct segmf_data *data = seg->s_data;
183	offset_t off;
184
185	if (addr < seg->s_base || addr + len > seg->s_base + seg->s_size ||
186	    (len & PAGEOFFSET) || ((uintptr_t)addr & PAGEOFFSET))
187		panic("segmf_unmap");
188
189	if (addr != seg->s_base || len != seg->s_size)
190		return (ENOTSUP);
191
192	hat_unload(seg->s_as->a_hat, addr, len,
193	    HAT_UNLOAD_UNMAP | HAT_UNLOAD_UNLOCK);
194
195	off = (offset_t)seg_page(seg, addr);
196
197	ASSERT(data->vp != NULL);
198
199	(void) VOP_DELMAP(VTOCVP(data->vp), off, seg->s_as, addr, len,
200	    data->prot, data->maxprot, MAP_SHARED, CRED(), NULL);
201
202	seg_free(seg);
203	return (0);
204}
205
206static void
207segmf_free(struct seg *seg)
208{
209	struct segmf_data *data = seg->s_data;
210	pgcnt_t npages = seg_pages(seg);
211
212	kmem_free(data->map, npages * sizeof (segmf_map_t));
213	VN_RELE(data->vp);
214	mutex_destroy(&data->lock);
215	kmem_free(data, sizeof (*data));
216}
217
218static int segmf_faultpage_debug = 0;
219/*ARGSUSED*/
220static int
221segmf_faultpage(struct hat *hat, struct seg *seg, caddr_t addr,
222    enum fault_type type, uint_t prot)
223{
224	struct segmf_data *data = seg->s_data;
225	uint_t hat_flags = HAT_LOAD_NOCONSIST;
226	mfn_t mfn;
227	x86pte_t pte;
228	segmf_map_t *map;
229	uint_t idx;
230
231
232	idx = seg_page(seg, addr);
233	map = &data->map[idx];
234	ASSERT(map->t_type == SEGMF_MAP_MFN);
235
236	mfn = map->u.m.m_mfn;
237
238	if (type == F_SOFTLOCK) {
239		mutex_enter(&freemem_lock);
240		data->softlockcnt++;
241		mutex_exit(&freemem_lock);
242		hat_flags |= HAT_LOAD_LOCK;
243	} else
244		hat_flags |= HAT_LOAD;
245
246	if (segmf_faultpage_debug > 0) {
247		uprintf("segmf_faultpage: addr %p domid %x mfn %lx prot %x\n",
248		    (void *)addr, data->domid, mfn, prot);
249		segmf_faultpage_debug--;
250	}
251
252	/*
253	 * Ask the HAT to load a throwaway mapping to page zero, then
254	 * overwrite it with our foreign domain mapping. It gets removed
255	 * later via hat_unload()
256	 */
257	hat_devload(hat, addr, MMU_PAGESIZE, (pfn_t)0,
258	    PROT_READ | HAT_UNORDERED_OK, hat_flags);
259
260	pte = mmu_ptob((x86pte_t)mfn) | PT_VALID | PT_USER | PT_FOREIGN;
261	if (prot & PROT_WRITE)
262		pte |= PT_WRITABLE;
263
264	if (HYPERVISOR_update_va_mapping_otherdomain((uintptr_t)addr, pte,
265	    UVMF_INVLPG | UVMF_ALL, data->domid) != 0) {
266		hat_flags = HAT_UNLOAD_UNMAP;
267
268		if (type == F_SOFTLOCK) {
269			hat_flags |= HAT_UNLOAD_UNLOCK;
270			mutex_enter(&freemem_lock);
271			data->softlockcnt--;
272			mutex_exit(&freemem_lock);
273		}
274
275		hat_unload(hat, addr, MMU_PAGESIZE, hat_flags);
276		return (FC_MAKE_ERR(EFAULT));
277	}
278
279	return (0);
280}
281
282static int
283seg_rw_to_prot(enum seg_rw rw)
284{
285	switch (rw) {
286	case S_READ:
287		return (PROT_READ);
288	case S_WRITE:
289		return (PROT_WRITE);
290	case S_EXEC:
291		return (PROT_EXEC);
292	case S_OTHER:
293	default:
294		break;
295	}
296	return (PROT_READ | PROT_WRITE | PROT_EXEC);
297}
298
299static void
300segmf_softunlock(struct hat *hat, struct seg *seg, caddr_t addr, size_t len)
301{
302	struct segmf_data *data = seg->s_data;
303
304	hat_unlock(hat, addr, len);
305
306	mutex_enter(&freemem_lock);
307	ASSERT(data->softlockcnt >= btopr(len));
308	data->softlockcnt -= btopr(len);
309	mutex_exit(&freemem_lock);
310
311	if (data->softlockcnt == 0) {
312		struct as *as = seg->s_as;
313
314		if (AS_ISUNMAPWAIT(as)) {
315			mutex_enter(&as->a_contents);
316			if (AS_ISUNMAPWAIT(as)) {
317				AS_CLRUNMAPWAIT(as);
318				cv_broadcast(&as->a_cv);
319			}
320			mutex_exit(&as->a_contents);
321		}
322	}
323}
324
325static int
326segmf_fault_range(struct hat *hat, struct seg *seg, caddr_t addr, size_t len,
327    enum fault_type type, enum seg_rw rw)
328{
329	struct segmf_data *data = seg->s_data;
330	int error = 0;
331	caddr_t a;
332
333	if ((data->prot & seg_rw_to_prot(rw)) == 0)
334		return (FC_PROT);
335
336	/* loop over the address range handling each fault */
337
338	for (a = addr; a < addr + len; a += PAGESIZE) {
339		error = segmf_faultpage(hat, seg, a, type, data->prot);
340		if (error != 0)
341			break;
342	}
343
344	if (error != 0 && type == F_SOFTLOCK) {
345		size_t done = (size_t)(a - addr);
346
347		/*
348		 * Undo what's been done so far.
349		 */
350		if (done > 0)
351			segmf_softunlock(hat, seg, addr, done);
352	}
353
354	return (error);
355}
356
357/*
358 * We never demand-fault for seg_mf.
359 */
360/*ARGSUSED*/
361static int
362segmf_fault(struct hat *hat, struct seg *seg, caddr_t addr, size_t len,
363    enum fault_type type, enum seg_rw rw)
364{
365	return (FC_MAKE_ERR(EFAULT));
366}
367
368/*ARGSUSED*/
369static int
370segmf_faulta(struct seg *seg, caddr_t addr)
371{
372	return (0);
373}
374
375/*ARGSUSED*/
376static int
377segmf_setprot(struct seg *seg, caddr_t addr, size_t len, uint_t prot)
378{
379	return (EINVAL);
380}
381
382/*ARGSUSED*/
383static int
384segmf_checkprot(struct seg *seg, caddr_t addr, size_t len, uint_t prot)
385{
386	return (EINVAL);
387}
388
389/*ARGSUSED*/
390static int
391segmf_kluster(struct seg *seg, caddr_t addr, ssize_t delta)
392{
393	return (-1);
394}
395
396/*ARGSUSED*/
397static int
398segmf_sync(struct seg *seg, caddr_t addr, size_t len, int attr, uint_t flags)
399{
400	return (0);
401}
402
403/*
404 * XXPV	Hmm.  Should we say that mf mapping are "in core?"
405 */
406
407/*ARGSUSED*/
408static size_t
409segmf_incore(struct seg *seg, caddr_t addr, size_t len, char *vec)
410{
411	size_t v;
412
413	for (v = 0, len = (len + PAGEOFFSET) & PAGEMASK; len;
414	    len -= PAGESIZE, v += PAGESIZE)
415		*vec++ = 1;
416	return (v);
417}
418
419/*ARGSUSED*/
420static int
421segmf_lockop(struct seg *seg, caddr_t addr,
422    size_t len, int attr, int op, ulong_t *lockmap, size_t pos)
423{
424	return (0);
425}
426
427static int
428segmf_getprot(struct seg *seg, caddr_t addr, size_t len, uint_t *protv)
429{
430	struct segmf_data *data = seg->s_data;
431	pgcnt_t pgno = seg_page(seg, addr + len) - seg_page(seg, addr) + 1;
432
433	if (pgno != 0) {
434		do
435			protv[--pgno] = data->prot;
436		while (pgno != 0)
437			;
438	}
439	return (0);
440}
441
442static u_offset_t
443segmf_getoffset(struct seg *seg, caddr_t addr)
444{
445	return (addr - seg->s_base);
446}
447
448/*ARGSUSED*/
449static int
450segmf_gettype(struct seg *seg, caddr_t addr)
451{
452	return (MAP_SHARED);
453}
454
455/*ARGSUSED1*/
456static int
457segmf_getvp(struct seg *seg, caddr_t addr, struct vnode **vpp)
458{
459	struct segmf_data *data = seg->s_data;
460
461	*vpp = VTOCVP(data->vp);
462	return (0);
463}
464
465/*ARGSUSED*/
466static int
467segmf_advise(struct seg *seg, caddr_t addr, size_t len, uint_t behav)
468{
469	return (0);
470}
471
472/*ARGSUSED*/
473static void
474segmf_dump(struct seg *seg)
475{}
476
477/*ARGSUSED*/
478static int
479segmf_pagelock(struct seg *seg, caddr_t addr, size_t len,
480    struct page ***ppp, enum lock_type type, enum seg_rw rw)
481{
482	return (ENOTSUP);
483}
484
485/*ARGSUSED*/
486static int
487segmf_setpagesize(struct seg *seg, caddr_t addr, size_t len, uint_t szc)
488{
489	return (ENOTSUP);
490}
491
492static int
493segmf_getmemid(struct seg *seg, caddr_t addr, memid_t *memid)
494{
495	struct segmf_data *data = seg->s_data;
496
497	memid->val[0] = (uintptr_t)VTOCVP(data->vp);
498	memid->val[1] = (uintptr_t)seg_page(seg, addr);
499	return (0);
500}
501
502/*ARGSUSED*/
503static lgrp_mem_policy_info_t *
504segmf_getpolicy(struct seg *seg, caddr_t addr)
505{
506	return (NULL);
507}
508
509/*ARGSUSED*/
510static int
511segmf_capable(struct seg *seg, segcapability_t capability)
512{
513	return (0);
514}
515
516/*
517 * Add a set of contiguous foreign MFNs to the segment. soft-locking them.  The
518 * pre-faulting is necessary due to live migration; in particular we must
519 * return an error in response to IOCTL_PRIVCMD_MMAPBATCH rather than faulting
520 * later on a bad MFN.  Whilst this isn't necessary for the other MMAP
521 * ioctl()s, we lock them too, as they should be transitory.
522 */
523int
524segmf_add_mfns(struct seg *seg, caddr_t addr, mfn_t mfn,
525    pgcnt_t pgcnt, domid_t domid)
526{
527	struct segmf_data *data = seg->s_data;
528	pgcnt_t base;
529	faultcode_t fc;
530	pgcnt_t i;
531	int error = 0;
532
533	if (seg->s_ops != &segmf_ops)
534		return (EINVAL);
535
536	/*
537	 * Don't mess with dom0.
538	 *
539	 * Only allow the domid to be set once for the segment.
540	 * After that attempts to add mappings to this segment for
541	 * other domains explicitly fails.
542	 */
543
544	if (domid == 0 || domid == DOMID_SELF)
545		return (EACCES);
546
547	mutex_enter(&data->lock);
548
549	if (data->domid == 0)
550		data->domid = domid;
551
552	if (data->domid != domid) {
553		error = EINVAL;
554		goto out;
555	}
556
557	base = seg_page(seg, addr);
558
559	for (i = 0; i < pgcnt; i++) {
560		data->map[base + i].t_type = SEGMF_MAP_MFN;
561		data->map[base + i].u.m.m_mfn = mfn++;
562	}
563
564	fc = segmf_fault_range(seg->s_as->a_hat, seg, addr,
565	    pgcnt * MMU_PAGESIZE, F_SOFTLOCK, S_OTHER);
566
567	if (fc != 0) {
568		error = fc_decode(fc);
569		for (i = 0; i < pgcnt; i++) {
570			data->map[base + i].t_type = SEGMF_MAP_EMPTY;
571		}
572	}
573
574out:
575	mutex_exit(&data->lock);
576	return (error);
577}
578
579int
580segmf_add_grefs(struct seg *seg, caddr_t addr, uint_t flags,
581    grant_ref_t *grefs, uint_t cnt, domid_t domid)
582{
583	struct segmf_data *data;
584	segmf_map_t *map;
585	faultcode_t fc;
586	uint_t idx;
587	uint_t i;
588	int e;
589
590	if (seg->s_ops != &segmf_ops)
591		return (EINVAL);
592
593	/*
594	 * Don't mess with dom0.
595	 *
596	 * Only allow the domid to be set once for the segment.
597	 * After that attempts to add mappings to this segment for
598	 * other domains explicitly fails.
599	 */
600
601	if (domid == 0 || domid == DOMID_SELF)
602		return (EACCES);
603
604	data = seg->s_data;
605	idx = seg_page(seg, addr);
606	map = &data->map[idx];
607	e = 0;
608
609	mutex_enter(&data->lock);
610
611	if (data->domid == 0)
612		data->domid = domid;
613
614	if (data->domid != domid) {
615		e = EINVAL;
616		goto out;
617	}
618
619	/* store away the grefs passed in then fault in the pages */
620	for (i = 0; i < cnt; i++) {
621		map[i].t_type = SEGMF_MAP_GREF;
622		map[i].u.g.g_gref = grefs[i];
623		map[i].u.g.g_handle = 0;
624		map[i].u.g.g_flags = 0;
625		if (flags & SEGMF_GREF_WR) {
626			map[i].u.g.g_flags |= SEGMF_GFLAGS_WR;
627		}
628	}
629	fc = segmf_fault_gref_range(seg, addr, cnt);
630	if (fc != 0) {
631		e = fc_decode(fc);
632		for (i = 0; i < cnt; i++) {
633			data->map[i].t_type = SEGMF_MAP_EMPTY;
634		}
635	}
636
637out:
638	mutex_exit(&data->lock);
639	return (e);
640}
641
642int
643segmf_release_grefs(struct seg *seg, caddr_t addr, uint_t cnt)
644{
645	gnttab_unmap_grant_ref_t mapop[SEGMF_MAX_GREFS];
646	struct segmf_data *data;
647	segmf_map_t *map;
648	uint_t idx;
649	long e;
650	int i;
651	int n;
652
653
654	if (cnt > SEGMF_MAX_GREFS) {
655		return (-1);
656	}
657
658	idx = seg_page(seg, addr);
659	data = seg->s_data;
660	map = &data->map[idx];
661
662	bzero(mapop, sizeof (gnttab_unmap_grant_ref_t) * cnt);
663
664	/*
665	 * for each entry which isn't empty and is currently mapped,
666	 * set it up for an unmap then mark them empty.
667	 */
668	n = 0;
669	for (i = 0; i < cnt; i++) {
670		ASSERT(map[i].t_type != SEGMF_MAP_MFN);
671		if ((map[i].t_type == SEGMF_MAP_GREF) &&
672		    (map[i].u.g.g_flags & SEGMF_GFLAGS_MAPPED)) {
673			mapop[n].handle = map[i].u.g.g_handle;
674			mapop[n].host_addr = map[i].u.g.g_ptep;
675			mapop[n].dev_bus_addr = 0;
676			n++;
677		}
678		map[i].t_type = SEGMF_MAP_EMPTY;
679	}
680
681	/* if there's nothing to unmap, just return */
682	if (n == 0) {
683		return (0);
684	}
685
686	e = HYPERVISOR_grant_table_op(GNTTABOP_unmap_grant_ref, &mapop, n);
687	if (e != 0) {
688		return (-1);
689	}
690
691	return (0);
692}
693
694
695void
696segmf_add_gref_pte(struct seg *seg, caddr_t addr, uint64_t pte_ma)
697{
698	struct segmf_data *data;
699	uint_t idx;
700
701	idx = seg_page(seg, addr);
702	data = seg->s_data;
703
704	data->map[idx].u.g.g_ptep = pte_ma;
705}
706
707
708static int
709segmf_fault_gref_range(struct seg *seg, caddr_t addr, size_t cnt)
710{
711	gnttab_map_grant_ref_t mapop[SEGMF_MAX_GREFS];
712	struct segmf_data *data;
713	segmf_map_t *map;
714	uint_t idx;
715	int e;
716	int i;
717
718
719	if (cnt > SEGMF_MAX_GREFS) {
720		return (-1);
721	}
722
723	data = seg->s_data;
724	idx = seg_page(seg, addr);
725	map = &data->map[idx];
726
727	bzero(mapop, sizeof (gnttab_map_grant_ref_t) * cnt);
728
729	ASSERT(map->t_type == SEGMF_MAP_GREF);
730
731	/*
732	 * map in each page passed in into the user apps AS. We do this by
733	 * passing the MA of the actual pte of the mapping to the hypervisor.
734	 */
735	for (i = 0; i < cnt; i++) {
736		mapop[i].host_addr = map[i].u.g.g_ptep;
737		mapop[i].dom = data->domid;
738		mapop[i].ref = map[i].u.g.g_gref;
739		mapop[i].flags = GNTMAP_host_map | GNTMAP_application_map |
740		    GNTMAP_contains_pte;
741		if (!(map[i].u.g.g_flags & SEGMF_GFLAGS_WR)) {
742			mapop[i].flags |= GNTMAP_readonly;
743		}
744	}
745	e = xen_map_gref(GNTTABOP_map_grant_ref, mapop, cnt, B_TRUE);
746	if ((e != 0) || (mapop[0].status != GNTST_okay)) {
747		return (FC_MAKE_ERR(EFAULT));
748	}
749
750	/* save handle for segmf_release_grefs() and mark it as mapped */
751	for (i = 0; i < cnt; i++) {
752		ASSERT(mapop[i].status == GNTST_okay);
753		map[i].u.g.g_handle = mapop[i].handle;
754		map[i].u.g.g_flags |= SEGMF_GFLAGS_MAPPED;
755	}
756
757	return (0);
758}
759
760static struct seg_ops segmf_ops = {
761	segmf_dup,
762	segmf_unmap,
763	segmf_free,
764	segmf_fault,
765	segmf_faulta,
766	segmf_setprot,
767	segmf_checkprot,
768	(int (*)())segmf_kluster,
769	(size_t (*)(struct seg *))NULL,	/* swapout */
770	segmf_sync,
771	segmf_incore,
772	segmf_lockop,
773	segmf_getprot,
774	segmf_getoffset,
775	segmf_gettype,
776	segmf_getvp,
777	segmf_advise,
778	segmf_dump,
779	segmf_pagelock,
780	segmf_setpagesize,
781	segmf_getmemid,
782	segmf_getpolicy,
783	segmf_capable
784};
785