libnvmm.c revision 1.14 1 1.14 maxv /* $NetBSD: libnvmm.c,v 1.14 2019/06/08 07:27:44 maxv Exp $ */
2 1.1 maxv
3 1.1 maxv /*
4 1.1 maxv * Copyright (c) 2018 The NetBSD Foundation, Inc.
5 1.1 maxv * All rights reserved.
6 1.1 maxv *
7 1.1 maxv * This code is derived from software contributed to The NetBSD Foundation
8 1.1 maxv * by Maxime Villard.
9 1.1 maxv *
10 1.1 maxv * Redistribution and use in source and binary forms, with or without
11 1.1 maxv * modification, are permitted provided that the following conditions
12 1.1 maxv * are met:
13 1.1 maxv * 1. Redistributions of source code must retain the above copyright
14 1.1 maxv * notice, this list of conditions and the following disclaimer.
15 1.1 maxv * 2. Redistributions in binary form must reproduce the above copyright
16 1.1 maxv * notice, this list of conditions and the following disclaimer in the
17 1.1 maxv * documentation and/or other materials provided with the distribution.
18 1.1 maxv *
19 1.1 maxv * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 1.1 maxv * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 1.1 maxv * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 1.1 maxv * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 1.1 maxv * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 1.1 maxv * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 1.1 maxv * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 1.1 maxv * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 1.1 maxv * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 1.1 maxv * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 1.1 maxv * POSSIBILITY OF SUCH DAMAGE.
30 1.1 maxv */
31 1.1 maxv
32 1.1 maxv #include <sys/cdefs.h>
33 1.1 maxv
34 1.1 maxv #include <stdio.h>
35 1.1 maxv #include <stdlib.h>
36 1.1 maxv #include <string.h>
37 1.1 maxv #include <unistd.h>
38 1.1 maxv #include <fcntl.h>
39 1.1 maxv #include <errno.h>
40 1.1 maxv #include <sys/ioctl.h>
41 1.1 maxv #include <sys/mman.h>
42 1.3 maxv #include <sys/queue.h>
43 1.10 maxv #include <machine/vmparam.h>
44 1.1 maxv
45 1.1 maxv #include "nvmm.h"
46 1.1 maxv
47 1.10 maxv static struct nvmm_capability __capability;
48 1.10 maxv
49 1.10 maxv #ifdef __x86_64__
50 1.10 maxv #include "libnvmm_x86.c"
51 1.10 maxv #endif
52 1.6 maxv
53 1.3 maxv typedef struct __area {
54 1.3 maxv LIST_ENTRY(__area) list;
55 1.3 maxv gpaddr_t gpa;
56 1.3 maxv uintptr_t hva;
57 1.3 maxv size_t size;
58 1.8 maxv nvmm_prot_t prot;
59 1.3 maxv } area_t;
60 1.3 maxv
61 1.3 maxv typedef LIST_HEAD(, __area) area_list_t;
62 1.3 maxv
63 1.1 maxv static int nvmm_fd = -1;
64 1.1 maxv
65 1.1 maxv /* -------------------------------------------------------------------------- */
66 1.1 maxv
67 1.4 maxv static bool
68 1.4 maxv __area_isvalid(struct nvmm_machine *mach, uintptr_t hva, gpaddr_t gpa,
69 1.1 maxv size_t size)
70 1.1 maxv {
71 1.3 maxv area_list_t *areas = mach->areas;
72 1.4 maxv area_t *ent;
73 1.3 maxv
74 1.4 maxv LIST_FOREACH(ent, areas, list) {
75 1.4 maxv /* Collision on GPA */
76 1.4 maxv if (gpa >= ent->gpa && gpa < ent->gpa + ent->size) {
77 1.4 maxv return false;
78 1.1 maxv }
79 1.5 maxv if (gpa + size > ent->gpa &&
80 1.5 maxv gpa + size <= ent->gpa + ent->size) {
81 1.4 maxv return false;
82 1.3 maxv }
83 1.4 maxv if (gpa <= ent->gpa && gpa + size >= ent->gpa + ent->size) {
84 1.4 maxv return false;
85 1.1 maxv }
86 1.1 maxv }
87 1.1 maxv
88 1.4 maxv return true;
89 1.3 maxv }
90 1.3 maxv
91 1.3 maxv static int
92 1.8 maxv __area_add(struct nvmm_machine *mach, uintptr_t hva, gpaddr_t gpa, size_t size,
93 1.8 maxv int prot)
94 1.3 maxv {
95 1.3 maxv area_list_t *areas = mach->areas;
96 1.8 maxv nvmm_prot_t nprot;
97 1.3 maxv area_t *area;
98 1.4 maxv
99 1.8 maxv nprot = 0;
100 1.8 maxv if (prot & PROT_READ)
101 1.8 maxv nprot |= NVMM_PROT_READ;
102 1.8 maxv if (prot & PROT_WRITE)
103 1.8 maxv nprot |= NVMM_PROT_WRITE;
104 1.8 maxv if (prot & PROT_EXEC)
105 1.8 maxv nprot |= NVMM_PROT_EXEC;
106 1.8 maxv
107 1.4 maxv if (!__area_isvalid(mach, hva, gpa, size)) {
108 1.4 maxv errno = EINVAL;
109 1.4 maxv return -1;
110 1.4 maxv }
111 1.3 maxv
112 1.3 maxv area = malloc(sizeof(*area));
113 1.3 maxv if (area == NULL)
114 1.1 maxv return -1;
115 1.1 maxv area->gpa = gpa;
116 1.1 maxv area->hva = hva;
117 1.1 maxv area->size = size;
118 1.8 maxv area->prot = nprot;
119 1.1 maxv
120 1.4 maxv LIST_INSERT_HEAD(areas, area, list);
121 1.4 maxv
122 1.4 maxv return 0;
123 1.4 maxv }
124 1.4 maxv
125 1.4 maxv static int
126 1.4 maxv __area_delete(struct nvmm_machine *mach, uintptr_t hva, gpaddr_t gpa,
127 1.4 maxv size_t size)
128 1.4 maxv {
129 1.4 maxv area_list_t *areas = mach->areas;
130 1.4 maxv area_t *ent, *nxt;
131 1.4 maxv
132 1.4 maxv LIST_FOREACH_SAFE(ent, areas, list, nxt) {
133 1.4 maxv if (hva == ent->hva && gpa == ent->gpa && size == ent->size) {
134 1.4 maxv LIST_REMOVE(ent, list);
135 1.4 maxv free(ent);
136 1.4 maxv return 0;
137 1.4 maxv }
138 1.3 maxv }
139 1.3 maxv
140 1.4 maxv return -1;
141 1.1 maxv }
142 1.1 maxv
143 1.3 maxv static void
144 1.3 maxv __area_remove_all(struct nvmm_machine *mach)
145 1.1 maxv {
146 1.3 maxv area_list_t *areas = mach->areas;
147 1.3 maxv area_t *ent;
148 1.1 maxv
149 1.3 maxv while ((ent = LIST_FIRST(areas)) != NULL) {
150 1.3 maxv LIST_REMOVE(ent, list);
151 1.3 maxv free(ent);
152 1.1 maxv }
153 1.1 maxv
154 1.3 maxv free(areas);
155 1.1 maxv }
156 1.1 maxv
157 1.1 maxv /* -------------------------------------------------------------------------- */
158 1.1 maxv
159 1.1 maxv static int
160 1.1 maxv nvmm_init(void)
161 1.1 maxv {
162 1.1 maxv if (nvmm_fd != -1)
163 1.1 maxv return 0;
164 1.1 maxv nvmm_fd = open("/dev/nvmm", O_RDWR);
165 1.1 maxv if (nvmm_fd == -1)
166 1.1 maxv return -1;
167 1.10 maxv if (nvmm_capability(&__capability) == -1) {
168 1.10 maxv close(nvmm_fd);
169 1.10 maxv nvmm_fd = -1;
170 1.10 maxv return -1;
171 1.10 maxv }
172 1.1 maxv return 0;
173 1.1 maxv }
174 1.1 maxv
175 1.1 maxv int
176 1.1 maxv nvmm_capability(struct nvmm_capability *cap)
177 1.1 maxv {
178 1.1 maxv struct nvmm_ioc_capability args;
179 1.1 maxv int ret;
180 1.1 maxv
181 1.1 maxv if (nvmm_init() == -1) {
182 1.1 maxv return -1;
183 1.1 maxv }
184 1.1 maxv
185 1.1 maxv ret = ioctl(nvmm_fd, NVMM_IOC_CAPABILITY, &args);
186 1.1 maxv if (ret == -1)
187 1.1 maxv return -1;
188 1.1 maxv
189 1.1 maxv memcpy(cap, &args.cap, sizeof(args.cap));
190 1.1 maxv
191 1.1 maxv return 0;
192 1.1 maxv }
193 1.1 maxv
194 1.1 maxv int
195 1.1 maxv nvmm_machine_create(struct nvmm_machine *mach)
196 1.1 maxv {
197 1.1 maxv struct nvmm_ioc_machine_create args;
198 1.10 maxv struct nvmm_comm_page **pages;
199 1.3 maxv area_list_t *areas;
200 1.1 maxv int ret;
201 1.1 maxv
202 1.1 maxv if (nvmm_init() == -1) {
203 1.1 maxv return -1;
204 1.1 maxv }
205 1.1 maxv
206 1.3 maxv areas = calloc(1, sizeof(*areas));
207 1.3 maxv if (areas == NULL)
208 1.3 maxv return -1;
209 1.3 maxv
210 1.10 maxv pages = calloc(__capability.max_vcpus, sizeof(*pages));
211 1.10 maxv if (pages == NULL) {
212 1.10 maxv free(areas);
213 1.10 maxv return -1;
214 1.10 maxv }
215 1.10 maxv
216 1.1 maxv ret = ioctl(nvmm_fd, NVMM_IOC_MACHINE_CREATE, &args);
217 1.3 maxv if (ret == -1) {
218 1.3 maxv free(areas);
219 1.1 maxv return -1;
220 1.3 maxv }
221 1.1 maxv
222 1.10 maxv LIST_INIT(areas);
223 1.10 maxv
224 1.1 maxv memset(mach, 0, sizeof(*mach));
225 1.10 maxv mach->machid = args.machid;
226 1.10 maxv mach->pages = pages;
227 1.3 maxv mach->areas = areas;
228 1.1 maxv
229 1.1 maxv return 0;
230 1.1 maxv }
231 1.1 maxv
232 1.1 maxv int
233 1.1 maxv nvmm_machine_destroy(struct nvmm_machine *mach)
234 1.1 maxv {
235 1.1 maxv struct nvmm_ioc_machine_destroy args;
236 1.1 maxv int ret;
237 1.1 maxv
238 1.1 maxv args.machid = mach->machid;
239 1.1 maxv
240 1.1 maxv ret = ioctl(nvmm_fd, NVMM_IOC_MACHINE_DESTROY, &args);
241 1.1 maxv if (ret == -1)
242 1.1 maxv return -1;
243 1.1 maxv
244 1.3 maxv __area_remove_all(mach);
245 1.10 maxv free(mach->pages);
246 1.1 maxv
247 1.1 maxv return 0;
248 1.1 maxv }
249 1.1 maxv
250 1.1 maxv int
251 1.1 maxv nvmm_machine_configure(struct nvmm_machine *mach, uint64_t op, void *conf)
252 1.1 maxv {
253 1.1 maxv struct nvmm_ioc_machine_configure args;
254 1.1 maxv int ret;
255 1.1 maxv
256 1.13 maxv switch (op) {
257 1.13 maxv case NVMM_MACH_CONF_CALLBACKS:
258 1.13 maxv memcpy(&mach->cbs, conf, sizeof(mach->cbs));
259 1.13 maxv return 0;
260 1.13 maxv }
261 1.13 maxv
262 1.1 maxv args.machid = mach->machid;
263 1.1 maxv args.op = op;
264 1.1 maxv args.conf = conf;
265 1.1 maxv
266 1.1 maxv ret = ioctl(nvmm_fd, NVMM_IOC_MACHINE_CONFIGURE, &args);
267 1.1 maxv if (ret == -1)
268 1.1 maxv return -1;
269 1.1 maxv
270 1.1 maxv return 0;
271 1.1 maxv }
272 1.1 maxv
273 1.1 maxv int
274 1.14 maxv nvmm_vcpu_create(struct nvmm_machine *mach, nvmm_cpuid_t cpuid,
275 1.14 maxv struct nvmm_vcpu *vcpu)
276 1.1 maxv {
277 1.1 maxv struct nvmm_ioc_vcpu_create args;
278 1.10 maxv struct nvmm_comm_page *comm;
279 1.1 maxv int ret;
280 1.1 maxv
281 1.1 maxv args.machid = mach->machid;
282 1.1 maxv args.cpuid = cpuid;
283 1.1 maxv
284 1.1 maxv ret = ioctl(nvmm_fd, NVMM_IOC_VCPU_CREATE, &args);
285 1.1 maxv if (ret == -1)
286 1.1 maxv return -1;
287 1.1 maxv
288 1.10 maxv comm = mmap(NULL, PAGE_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_FILE,
289 1.10 maxv nvmm_fd, NVMM_COMM_OFF(mach->machid, cpuid));
290 1.10 maxv if (comm == MAP_FAILED)
291 1.10 maxv return -1;
292 1.10 maxv
293 1.10 maxv mach->pages[cpuid] = comm;
294 1.10 maxv
295 1.14 maxv vcpu->cpuid = cpuid;
296 1.14 maxv vcpu->state = &comm->state;
297 1.14 maxv vcpu->event = &comm->event;
298 1.14 maxv vcpu->exit = malloc(sizeof(*vcpu->exit));
299 1.14 maxv
300 1.1 maxv return 0;
301 1.1 maxv }
302 1.1 maxv
303 1.1 maxv int
304 1.14 maxv nvmm_vcpu_destroy(struct nvmm_machine *mach, struct nvmm_vcpu *vcpu)
305 1.1 maxv {
306 1.1 maxv struct nvmm_ioc_vcpu_destroy args;
307 1.10 maxv struct nvmm_comm_page *comm;
308 1.1 maxv int ret;
309 1.1 maxv
310 1.1 maxv args.machid = mach->machid;
311 1.14 maxv args.cpuid = vcpu->cpuid;
312 1.1 maxv
313 1.1 maxv ret = ioctl(nvmm_fd, NVMM_IOC_VCPU_DESTROY, &args);
314 1.1 maxv if (ret == -1)
315 1.1 maxv return -1;
316 1.1 maxv
317 1.14 maxv comm = mach->pages[vcpu->cpuid];
318 1.10 maxv munmap(comm, PAGE_SIZE);
319 1.14 maxv free(vcpu->exit);
320 1.10 maxv
321 1.1 maxv return 0;
322 1.1 maxv }
323 1.1 maxv
324 1.1 maxv int
325 1.14 maxv nvmm_vcpu_setstate(struct nvmm_machine *mach, struct nvmm_vcpu *vcpu,
326 1.14 maxv uint64_t flags)
327 1.1 maxv {
328 1.10 maxv struct nvmm_comm_page *comm;
329 1.1 maxv
330 1.14 maxv comm = mach->pages[vcpu->cpuid];
331 1.10 maxv comm->state_commit |= flags;
332 1.10 maxv comm->state_cached |= flags;
333 1.1 maxv
334 1.1 maxv return 0;
335 1.1 maxv }
336 1.1 maxv
337 1.1 maxv int
338 1.14 maxv nvmm_vcpu_getstate(struct nvmm_machine *mach, struct nvmm_vcpu *vcpu,
339 1.14 maxv uint64_t flags)
340 1.1 maxv {
341 1.1 maxv struct nvmm_ioc_vcpu_getstate args;
342 1.10 maxv struct nvmm_comm_page *comm;
343 1.1 maxv int ret;
344 1.1 maxv
345 1.14 maxv comm = mach->pages[vcpu->cpuid];
346 1.10 maxv
347 1.10 maxv if (__predict_true((flags & ~comm->state_cached) == 0)) {
348 1.14 maxv return 0;
349 1.10 maxv }
350 1.10 maxv comm->state_wanted = flags & ~comm->state_cached;
351 1.10 maxv
352 1.1 maxv args.machid = mach->machid;
353 1.14 maxv args.cpuid = vcpu->cpuid;
354 1.1 maxv
355 1.1 maxv ret = ioctl(nvmm_fd, NVMM_IOC_VCPU_GETSTATE, &args);
356 1.1 maxv if (ret == -1)
357 1.1 maxv return -1;
358 1.1 maxv
359 1.1 maxv return 0;
360 1.1 maxv }
361 1.1 maxv
362 1.1 maxv int
363 1.14 maxv nvmm_vcpu_inject(struct nvmm_machine *mach, struct nvmm_vcpu *vcpu)
364 1.1 maxv {
365 1.12 maxv struct nvmm_comm_page *comm;
366 1.1 maxv
367 1.14 maxv comm = mach->pages[vcpu->cpuid];
368 1.12 maxv comm->event_commit = true;
369 1.1 maxv
370 1.1 maxv return 0;
371 1.1 maxv }
372 1.1 maxv
373 1.1 maxv int
374 1.14 maxv nvmm_vcpu_run(struct nvmm_machine *mach, struct nvmm_vcpu *vcpu)
375 1.1 maxv {
376 1.1 maxv struct nvmm_ioc_vcpu_run args;
377 1.1 maxv int ret;
378 1.1 maxv
379 1.1 maxv args.machid = mach->machid;
380 1.14 maxv args.cpuid = vcpu->cpuid;
381 1.1 maxv memset(&args.exit, 0, sizeof(args.exit));
382 1.1 maxv
383 1.1 maxv ret = ioctl(nvmm_fd, NVMM_IOC_VCPU_RUN, &args);
384 1.1 maxv if (ret == -1)
385 1.1 maxv return -1;
386 1.1 maxv
387 1.14 maxv /* No comm support yet, just copy. */
388 1.14 maxv memcpy(vcpu->exit, &args.exit, sizeof(args.exit));
389 1.1 maxv
390 1.1 maxv return 0;
391 1.1 maxv }
392 1.1 maxv
393 1.1 maxv int
394 1.1 maxv nvmm_gpa_map(struct nvmm_machine *mach, uintptr_t hva, gpaddr_t gpa,
395 1.7 maxv size_t size, int prot)
396 1.1 maxv {
397 1.1 maxv struct nvmm_ioc_gpa_map args;
398 1.1 maxv int ret;
399 1.1 maxv
400 1.8 maxv ret = __area_add(mach, hva, gpa, size, prot);
401 1.3 maxv if (ret == -1)
402 1.3 maxv return -1;
403 1.3 maxv
404 1.1 maxv args.machid = mach->machid;
405 1.1 maxv args.hva = hva;
406 1.1 maxv args.gpa = gpa;
407 1.1 maxv args.size = size;
408 1.7 maxv args.prot = prot;
409 1.1 maxv
410 1.1 maxv ret = ioctl(nvmm_fd, NVMM_IOC_GPA_MAP, &args);
411 1.1 maxv if (ret == -1) {
412 1.3 maxv /* Can't recover. */
413 1.3 maxv abort();
414 1.1 maxv }
415 1.1 maxv
416 1.1 maxv return 0;
417 1.1 maxv }
418 1.1 maxv
419 1.1 maxv int
420 1.1 maxv nvmm_gpa_unmap(struct nvmm_machine *mach, uintptr_t hva, gpaddr_t gpa,
421 1.1 maxv size_t size)
422 1.1 maxv {
423 1.4 maxv struct nvmm_ioc_gpa_unmap args;
424 1.4 maxv int ret;
425 1.4 maxv
426 1.4 maxv ret = __area_delete(mach, hva, gpa, size);
427 1.4 maxv if (ret == -1)
428 1.4 maxv return -1;
429 1.4 maxv
430 1.4 maxv args.machid = mach->machid;
431 1.4 maxv args.gpa = gpa;
432 1.4 maxv args.size = size;
433 1.4 maxv
434 1.4 maxv ret = ioctl(nvmm_fd, NVMM_IOC_GPA_UNMAP, &args);
435 1.5 maxv if (ret == -1) {
436 1.5 maxv /* Can't recover. */
437 1.5 maxv abort();
438 1.5 maxv }
439 1.5 maxv
440 1.5 maxv return 0;
441 1.5 maxv }
442 1.5 maxv
443 1.5 maxv int
444 1.5 maxv nvmm_hva_map(struct nvmm_machine *mach, uintptr_t hva, size_t size)
445 1.5 maxv {
446 1.5 maxv struct nvmm_ioc_hva_map args;
447 1.5 maxv int ret;
448 1.5 maxv
449 1.5 maxv args.machid = mach->machid;
450 1.5 maxv args.hva = hva;
451 1.5 maxv args.size = size;
452 1.5 maxv
453 1.5 maxv ret = ioctl(nvmm_fd, NVMM_IOC_HVA_MAP, &args);
454 1.4 maxv if (ret == -1)
455 1.4 maxv return -1;
456 1.4 maxv
457 1.5 maxv return 0;
458 1.5 maxv }
459 1.4 maxv
460 1.5 maxv int
461 1.5 maxv nvmm_hva_unmap(struct nvmm_machine *mach, uintptr_t hva, size_t size)
462 1.5 maxv {
463 1.6 maxv struct nvmm_ioc_hva_unmap args;
464 1.5 maxv int ret;
465 1.5 maxv
466 1.5 maxv args.machid = mach->machid;
467 1.5 maxv args.hva = hva;
468 1.5 maxv args.size = size;
469 1.5 maxv
470 1.6 maxv ret = ioctl(nvmm_fd, NVMM_IOC_HVA_UNMAP, &args);
471 1.5 maxv if (ret == -1)
472 1.5 maxv return -1;
473 1.5 maxv
474 1.5 maxv return 0;
475 1.1 maxv }
476 1.1 maxv
477 1.1 maxv /*
478 1.1 maxv * nvmm_gva_to_gpa(): architecture-specific.
479 1.1 maxv */
480 1.1 maxv
481 1.1 maxv int
482 1.8 maxv nvmm_gpa_to_hva(struct nvmm_machine *mach, gpaddr_t gpa, uintptr_t *hva,
483 1.8 maxv nvmm_prot_t *prot)
484 1.1 maxv {
485 1.3 maxv area_list_t *areas = mach->areas;
486 1.3 maxv area_t *ent;
487 1.1 maxv
488 1.3 maxv LIST_FOREACH(ent, areas, list) {
489 1.5 maxv if (gpa >= ent->gpa && gpa < ent->gpa + ent->size) {
490 1.5 maxv *hva = ent->hva + (gpa - ent->gpa);
491 1.8 maxv *prot = ent->prot;
492 1.5 maxv return 0;
493 1.1 maxv }
494 1.1 maxv }
495 1.1 maxv
496 1.1 maxv errno = ENOENT;
497 1.1 maxv return -1;
498 1.1 maxv }
499 1.1 maxv
500 1.1 maxv /*
501 1.1 maxv * nvmm_assist_io(): architecture-specific.
502 1.1 maxv */
503 1.6 maxv
504 1.6 maxv /*
505 1.6 maxv * nvmm_assist_mem(): architecture-specific.
506 1.6 maxv */
507 1.6 maxv
508 1.9 maxv int
509 1.9 maxv nvmm_ctl(int op, void *data, size_t size)
510 1.9 maxv {
511 1.9 maxv struct nvmm_ioc_ctl args;
512 1.9 maxv int ret;
513 1.9 maxv
514 1.9 maxv if (nvmm_init() == -1) {
515 1.9 maxv return -1;
516 1.9 maxv }
517 1.9 maxv
518 1.9 maxv args.op = op;
519 1.9 maxv args.data = data;
520 1.9 maxv args.size = size;
521 1.9 maxv
522 1.9 maxv ret = ioctl(nvmm_fd, NVMM_IOC_CTL, &args);
523 1.9 maxv if (ret == -1)
524 1.9 maxv return -1;
525 1.9 maxv
526 1.9 maxv return 0;
527 1.9 maxv }
528