1*a0e2cb6aSTiwei Bie // SPDX-License-Identifier: GPL-2.0
2*a0e2cb6aSTiwei Bie /*
3*a0e2cb6aSTiwei Bie * Copyright (C) 2025 Ant Group
4*a0e2cb6aSTiwei Bie * Author: Tiwei Bie <tiwei.btw@antgroup.com>
5*a0e2cb6aSTiwei Bie */
6*a0e2cb6aSTiwei Bie #include <errno.h>
7*a0e2cb6aSTiwei Bie #include <fcntl.h>
8*a0e2cb6aSTiwei Bie #include <unistd.h>
9*a0e2cb6aSTiwei Bie #include <stdio.h>
10*a0e2cb6aSTiwei Bie #include <stdint.h>
11*a0e2cb6aSTiwei Bie #include <stdlib.h>
12*a0e2cb6aSTiwei Bie #include <string.h>
13*a0e2cb6aSTiwei Bie #include <sys/ioctl.h>
14*a0e2cb6aSTiwei Bie #include <sys/eventfd.h>
15*a0e2cb6aSTiwei Bie #include <linux/limits.h>
16*a0e2cb6aSTiwei Bie #include <linux/vfio.h>
17*a0e2cb6aSTiwei Bie #include <linux/pci_regs.h>
18*a0e2cb6aSTiwei Bie #include <as-layout.h>
19*a0e2cb6aSTiwei Bie #include <um_malloc.h>
20*a0e2cb6aSTiwei Bie
21*a0e2cb6aSTiwei Bie #include "vfio_user.h"
22*a0e2cb6aSTiwei Bie
uml_vfio_user_open_container(void)23*a0e2cb6aSTiwei Bie int uml_vfio_user_open_container(void)
24*a0e2cb6aSTiwei Bie {
25*a0e2cb6aSTiwei Bie int r, fd;
26*a0e2cb6aSTiwei Bie
27*a0e2cb6aSTiwei Bie fd = open("/dev/vfio/vfio", O_RDWR);
28*a0e2cb6aSTiwei Bie if (fd < 0)
29*a0e2cb6aSTiwei Bie return -errno;
30*a0e2cb6aSTiwei Bie
31*a0e2cb6aSTiwei Bie r = ioctl(fd, VFIO_GET_API_VERSION);
32*a0e2cb6aSTiwei Bie if (r != VFIO_API_VERSION) {
33*a0e2cb6aSTiwei Bie r = r < 0 ? -errno : -EINVAL;
34*a0e2cb6aSTiwei Bie goto error;
35*a0e2cb6aSTiwei Bie }
36*a0e2cb6aSTiwei Bie
37*a0e2cb6aSTiwei Bie r = ioctl(fd, VFIO_CHECK_EXTENSION, VFIO_TYPE1_IOMMU);
38*a0e2cb6aSTiwei Bie if (r <= 0) {
39*a0e2cb6aSTiwei Bie r = r < 0 ? -errno : -EINVAL;
40*a0e2cb6aSTiwei Bie goto error;
41*a0e2cb6aSTiwei Bie }
42*a0e2cb6aSTiwei Bie
43*a0e2cb6aSTiwei Bie return fd;
44*a0e2cb6aSTiwei Bie
45*a0e2cb6aSTiwei Bie error:
46*a0e2cb6aSTiwei Bie close(fd);
47*a0e2cb6aSTiwei Bie return r;
48*a0e2cb6aSTiwei Bie }
49*a0e2cb6aSTiwei Bie
uml_vfio_user_setup_iommu(int container)50*a0e2cb6aSTiwei Bie int uml_vfio_user_setup_iommu(int container)
51*a0e2cb6aSTiwei Bie {
52*a0e2cb6aSTiwei Bie /*
53*a0e2cb6aSTiwei Bie * This is a bit tricky. See the big comment in
54*a0e2cb6aSTiwei Bie * vhost_user_set_mem_table() in virtio_uml.c.
55*a0e2cb6aSTiwei Bie */
56*a0e2cb6aSTiwei Bie unsigned long reserved = uml_reserved - uml_physmem;
57*a0e2cb6aSTiwei Bie struct vfio_iommu_type1_dma_map dma_map = {
58*a0e2cb6aSTiwei Bie .argsz = sizeof(dma_map),
59*a0e2cb6aSTiwei Bie .flags = VFIO_DMA_MAP_FLAG_READ | VFIO_DMA_MAP_FLAG_WRITE,
60*a0e2cb6aSTiwei Bie .vaddr = uml_reserved,
61*a0e2cb6aSTiwei Bie .iova = reserved,
62*a0e2cb6aSTiwei Bie .size = physmem_size - reserved,
63*a0e2cb6aSTiwei Bie };
64*a0e2cb6aSTiwei Bie
65*a0e2cb6aSTiwei Bie if (ioctl(container, VFIO_SET_IOMMU, VFIO_TYPE1_IOMMU) < 0)
66*a0e2cb6aSTiwei Bie return -errno;
67*a0e2cb6aSTiwei Bie
68*a0e2cb6aSTiwei Bie if (ioctl(container, VFIO_IOMMU_MAP_DMA, &dma_map) < 0)
69*a0e2cb6aSTiwei Bie return -errno;
70*a0e2cb6aSTiwei Bie
71*a0e2cb6aSTiwei Bie return 0;
72*a0e2cb6aSTiwei Bie }
73*a0e2cb6aSTiwei Bie
uml_vfio_user_get_group_id(const char * device)74*a0e2cb6aSTiwei Bie int uml_vfio_user_get_group_id(const char *device)
75*a0e2cb6aSTiwei Bie {
76*a0e2cb6aSTiwei Bie char *path, *buf, *end;
77*a0e2cb6aSTiwei Bie const char *name;
78*a0e2cb6aSTiwei Bie int r;
79*a0e2cb6aSTiwei Bie
80*a0e2cb6aSTiwei Bie path = uml_kmalloc(PATH_MAX, UM_GFP_KERNEL);
81*a0e2cb6aSTiwei Bie if (!path)
82*a0e2cb6aSTiwei Bie return -ENOMEM;
83*a0e2cb6aSTiwei Bie
84*a0e2cb6aSTiwei Bie sprintf(path, "/sys/bus/pci/devices/%s/iommu_group", device);
85*a0e2cb6aSTiwei Bie
86*a0e2cb6aSTiwei Bie buf = uml_kmalloc(PATH_MAX + 1, UM_GFP_KERNEL);
87*a0e2cb6aSTiwei Bie if (!buf) {
88*a0e2cb6aSTiwei Bie r = -ENOMEM;
89*a0e2cb6aSTiwei Bie goto free_path;
90*a0e2cb6aSTiwei Bie }
91*a0e2cb6aSTiwei Bie
92*a0e2cb6aSTiwei Bie r = readlink(path, buf, PATH_MAX);
93*a0e2cb6aSTiwei Bie if (r < 0) {
94*a0e2cb6aSTiwei Bie r = -errno;
95*a0e2cb6aSTiwei Bie goto free_buf;
96*a0e2cb6aSTiwei Bie }
97*a0e2cb6aSTiwei Bie buf[r] = '\0';
98*a0e2cb6aSTiwei Bie
99*a0e2cb6aSTiwei Bie name = basename(buf);
100*a0e2cb6aSTiwei Bie
101*a0e2cb6aSTiwei Bie r = strtoul(name, &end, 10);
102*a0e2cb6aSTiwei Bie if (*end != '\0' || end == name) {
103*a0e2cb6aSTiwei Bie r = -EINVAL;
104*a0e2cb6aSTiwei Bie goto free_buf;
105*a0e2cb6aSTiwei Bie }
106*a0e2cb6aSTiwei Bie
107*a0e2cb6aSTiwei Bie free_buf:
108*a0e2cb6aSTiwei Bie kfree(buf);
109*a0e2cb6aSTiwei Bie free_path:
110*a0e2cb6aSTiwei Bie kfree(path);
111*a0e2cb6aSTiwei Bie return r;
112*a0e2cb6aSTiwei Bie }
113*a0e2cb6aSTiwei Bie
uml_vfio_user_open_group(int group_id)114*a0e2cb6aSTiwei Bie int uml_vfio_user_open_group(int group_id)
115*a0e2cb6aSTiwei Bie {
116*a0e2cb6aSTiwei Bie char *path;
117*a0e2cb6aSTiwei Bie int fd;
118*a0e2cb6aSTiwei Bie
119*a0e2cb6aSTiwei Bie path = uml_kmalloc(PATH_MAX, UM_GFP_KERNEL);
120*a0e2cb6aSTiwei Bie if (!path)
121*a0e2cb6aSTiwei Bie return -ENOMEM;
122*a0e2cb6aSTiwei Bie
123*a0e2cb6aSTiwei Bie sprintf(path, "/dev/vfio/%d", group_id);
124*a0e2cb6aSTiwei Bie
125*a0e2cb6aSTiwei Bie fd = open(path, O_RDWR);
126*a0e2cb6aSTiwei Bie if (fd < 0) {
127*a0e2cb6aSTiwei Bie fd = -errno;
128*a0e2cb6aSTiwei Bie goto out;
129*a0e2cb6aSTiwei Bie }
130*a0e2cb6aSTiwei Bie
131*a0e2cb6aSTiwei Bie out:
132*a0e2cb6aSTiwei Bie kfree(path);
133*a0e2cb6aSTiwei Bie return fd;
134*a0e2cb6aSTiwei Bie }
135*a0e2cb6aSTiwei Bie
uml_vfio_user_set_container(int container,int group)136*a0e2cb6aSTiwei Bie int uml_vfio_user_set_container(int container, int group)
137*a0e2cb6aSTiwei Bie {
138*a0e2cb6aSTiwei Bie if (ioctl(group, VFIO_GROUP_SET_CONTAINER, &container) < 0)
139*a0e2cb6aSTiwei Bie return -errno;
140*a0e2cb6aSTiwei Bie return 0;
141*a0e2cb6aSTiwei Bie }
142*a0e2cb6aSTiwei Bie
uml_vfio_user_unset_container(int container,int group)143*a0e2cb6aSTiwei Bie int uml_vfio_user_unset_container(int container, int group)
144*a0e2cb6aSTiwei Bie {
145*a0e2cb6aSTiwei Bie if (ioctl(group, VFIO_GROUP_UNSET_CONTAINER, &container) < 0)
146*a0e2cb6aSTiwei Bie return -errno;
147*a0e2cb6aSTiwei Bie return 0;
148*a0e2cb6aSTiwei Bie }
149*a0e2cb6aSTiwei Bie
vfio_set_irqs(int device,int start,int count,int * irqfd)150*a0e2cb6aSTiwei Bie static int vfio_set_irqs(int device, int start, int count, int *irqfd)
151*a0e2cb6aSTiwei Bie {
152*a0e2cb6aSTiwei Bie struct vfio_irq_set *irq_set;
153*a0e2cb6aSTiwei Bie int argsz = sizeof(*irq_set) + sizeof(*irqfd) * count;
154*a0e2cb6aSTiwei Bie int err = 0;
155*a0e2cb6aSTiwei Bie
156*a0e2cb6aSTiwei Bie irq_set = uml_kmalloc(argsz, UM_GFP_KERNEL);
157*a0e2cb6aSTiwei Bie if (!irq_set)
158*a0e2cb6aSTiwei Bie return -ENOMEM;
159*a0e2cb6aSTiwei Bie
160*a0e2cb6aSTiwei Bie irq_set->argsz = argsz;
161*a0e2cb6aSTiwei Bie irq_set->flags = VFIO_IRQ_SET_DATA_EVENTFD | VFIO_IRQ_SET_ACTION_TRIGGER;
162*a0e2cb6aSTiwei Bie irq_set->index = VFIO_PCI_MSIX_IRQ_INDEX;
163*a0e2cb6aSTiwei Bie irq_set->start = start;
164*a0e2cb6aSTiwei Bie irq_set->count = count;
165*a0e2cb6aSTiwei Bie memcpy(irq_set->data, irqfd, sizeof(*irqfd) * count);
166*a0e2cb6aSTiwei Bie
167*a0e2cb6aSTiwei Bie if (ioctl(device, VFIO_DEVICE_SET_IRQS, irq_set) < 0) {
168*a0e2cb6aSTiwei Bie err = -errno;
169*a0e2cb6aSTiwei Bie goto out;
170*a0e2cb6aSTiwei Bie }
171*a0e2cb6aSTiwei Bie
172*a0e2cb6aSTiwei Bie out:
173*a0e2cb6aSTiwei Bie kfree(irq_set);
174*a0e2cb6aSTiwei Bie return err;
175*a0e2cb6aSTiwei Bie }
176*a0e2cb6aSTiwei Bie
uml_vfio_user_setup_device(struct uml_vfio_user_device * dev,int group,const char * device)177*a0e2cb6aSTiwei Bie int uml_vfio_user_setup_device(struct uml_vfio_user_device *dev,
178*a0e2cb6aSTiwei Bie int group, const char *device)
179*a0e2cb6aSTiwei Bie {
180*a0e2cb6aSTiwei Bie struct vfio_device_info device_info = { .argsz = sizeof(device_info) };
181*a0e2cb6aSTiwei Bie struct vfio_irq_info irq_info = { .argsz = sizeof(irq_info) };
182*a0e2cb6aSTiwei Bie int err, i;
183*a0e2cb6aSTiwei Bie
184*a0e2cb6aSTiwei Bie dev->device = ioctl(group, VFIO_GROUP_GET_DEVICE_FD, device);
185*a0e2cb6aSTiwei Bie if (dev->device < 0)
186*a0e2cb6aSTiwei Bie return -errno;
187*a0e2cb6aSTiwei Bie
188*a0e2cb6aSTiwei Bie if (ioctl(dev->device, VFIO_DEVICE_GET_INFO, &device_info) < 0) {
189*a0e2cb6aSTiwei Bie err = -errno;
190*a0e2cb6aSTiwei Bie goto close_device;
191*a0e2cb6aSTiwei Bie }
192*a0e2cb6aSTiwei Bie
193*a0e2cb6aSTiwei Bie dev->num_regions = device_info.num_regions;
194*a0e2cb6aSTiwei Bie if (dev->num_regions > VFIO_PCI_CONFIG_REGION_INDEX + 1)
195*a0e2cb6aSTiwei Bie dev->num_regions = VFIO_PCI_CONFIG_REGION_INDEX + 1;
196*a0e2cb6aSTiwei Bie
197*a0e2cb6aSTiwei Bie dev->region = uml_kmalloc(sizeof(*dev->region) * dev->num_regions,
198*a0e2cb6aSTiwei Bie UM_GFP_KERNEL);
199*a0e2cb6aSTiwei Bie if (!dev->region) {
200*a0e2cb6aSTiwei Bie err = -ENOMEM;
201*a0e2cb6aSTiwei Bie goto close_device;
202*a0e2cb6aSTiwei Bie }
203*a0e2cb6aSTiwei Bie
204*a0e2cb6aSTiwei Bie for (i = 0; i < dev->num_regions; i++) {
205*a0e2cb6aSTiwei Bie struct vfio_region_info region = {
206*a0e2cb6aSTiwei Bie .argsz = sizeof(region),
207*a0e2cb6aSTiwei Bie .index = i,
208*a0e2cb6aSTiwei Bie };
209*a0e2cb6aSTiwei Bie if (ioctl(dev->device, VFIO_DEVICE_GET_REGION_INFO, ®ion) < 0) {
210*a0e2cb6aSTiwei Bie err = -errno;
211*a0e2cb6aSTiwei Bie goto free_region;
212*a0e2cb6aSTiwei Bie }
213*a0e2cb6aSTiwei Bie dev->region[i].size = region.size;
214*a0e2cb6aSTiwei Bie dev->region[i].offset = region.offset;
215*a0e2cb6aSTiwei Bie }
216*a0e2cb6aSTiwei Bie
217*a0e2cb6aSTiwei Bie /* Only MSI-X is supported currently. */
218*a0e2cb6aSTiwei Bie irq_info.index = VFIO_PCI_MSIX_IRQ_INDEX;
219*a0e2cb6aSTiwei Bie if (ioctl(dev->device, VFIO_DEVICE_GET_IRQ_INFO, &irq_info) < 0) {
220*a0e2cb6aSTiwei Bie err = -errno;
221*a0e2cb6aSTiwei Bie goto free_region;
222*a0e2cb6aSTiwei Bie }
223*a0e2cb6aSTiwei Bie
224*a0e2cb6aSTiwei Bie dev->irq_count = irq_info.count;
225*a0e2cb6aSTiwei Bie
226*a0e2cb6aSTiwei Bie dev->irqfd = uml_kmalloc(sizeof(int) * dev->irq_count, UM_GFP_KERNEL);
227*a0e2cb6aSTiwei Bie if (!dev->irqfd) {
228*a0e2cb6aSTiwei Bie err = -ENOMEM;
229*a0e2cb6aSTiwei Bie goto free_region;
230*a0e2cb6aSTiwei Bie }
231*a0e2cb6aSTiwei Bie
232*a0e2cb6aSTiwei Bie memset(dev->irqfd, -1, sizeof(int) * dev->irq_count);
233*a0e2cb6aSTiwei Bie
234*a0e2cb6aSTiwei Bie err = vfio_set_irqs(dev->device, 0, dev->irq_count, dev->irqfd);
235*a0e2cb6aSTiwei Bie if (err)
236*a0e2cb6aSTiwei Bie goto free_irqfd;
237*a0e2cb6aSTiwei Bie
238*a0e2cb6aSTiwei Bie return 0;
239*a0e2cb6aSTiwei Bie
240*a0e2cb6aSTiwei Bie free_irqfd:
241*a0e2cb6aSTiwei Bie kfree(dev->irqfd);
242*a0e2cb6aSTiwei Bie free_region:
243*a0e2cb6aSTiwei Bie kfree(dev->region);
244*a0e2cb6aSTiwei Bie close_device:
245*a0e2cb6aSTiwei Bie close(dev->device);
246*a0e2cb6aSTiwei Bie return err;
247*a0e2cb6aSTiwei Bie }
248*a0e2cb6aSTiwei Bie
uml_vfio_user_teardown_device(struct uml_vfio_user_device * dev)249*a0e2cb6aSTiwei Bie void uml_vfio_user_teardown_device(struct uml_vfio_user_device *dev)
250*a0e2cb6aSTiwei Bie {
251*a0e2cb6aSTiwei Bie kfree(dev->irqfd);
252*a0e2cb6aSTiwei Bie kfree(dev->region);
253*a0e2cb6aSTiwei Bie close(dev->device);
254*a0e2cb6aSTiwei Bie }
255*a0e2cb6aSTiwei Bie
uml_vfio_user_activate_irq(struct uml_vfio_user_device * dev,int index)256*a0e2cb6aSTiwei Bie int uml_vfio_user_activate_irq(struct uml_vfio_user_device *dev, int index)
257*a0e2cb6aSTiwei Bie {
258*a0e2cb6aSTiwei Bie int irqfd;
259*a0e2cb6aSTiwei Bie
260*a0e2cb6aSTiwei Bie irqfd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
261*a0e2cb6aSTiwei Bie if (irqfd < 0)
262*a0e2cb6aSTiwei Bie return -errno;
263*a0e2cb6aSTiwei Bie
264*a0e2cb6aSTiwei Bie dev->irqfd[index] = irqfd;
265*a0e2cb6aSTiwei Bie return irqfd;
266*a0e2cb6aSTiwei Bie }
267*a0e2cb6aSTiwei Bie
uml_vfio_user_deactivate_irq(struct uml_vfio_user_device * dev,int index)268*a0e2cb6aSTiwei Bie void uml_vfio_user_deactivate_irq(struct uml_vfio_user_device *dev, int index)
269*a0e2cb6aSTiwei Bie {
270*a0e2cb6aSTiwei Bie close(dev->irqfd[index]);
271*a0e2cb6aSTiwei Bie dev->irqfd[index] = -1;
272*a0e2cb6aSTiwei Bie }
273*a0e2cb6aSTiwei Bie
uml_vfio_user_update_irqs(struct uml_vfio_user_device * dev)274*a0e2cb6aSTiwei Bie int uml_vfio_user_update_irqs(struct uml_vfio_user_device *dev)
275*a0e2cb6aSTiwei Bie {
276*a0e2cb6aSTiwei Bie return vfio_set_irqs(dev->device, 0, dev->irq_count, dev->irqfd);
277*a0e2cb6aSTiwei Bie }
278*a0e2cb6aSTiwei Bie
vfio_region_read(struct uml_vfio_user_device * dev,unsigned int index,uint64_t offset,void * buf,uint64_t size)279*a0e2cb6aSTiwei Bie static int vfio_region_read(struct uml_vfio_user_device *dev, unsigned int index,
280*a0e2cb6aSTiwei Bie uint64_t offset, void *buf, uint64_t size)
281*a0e2cb6aSTiwei Bie {
282*a0e2cb6aSTiwei Bie if (index >= dev->num_regions || offset + size > dev->region[index].size)
283*a0e2cb6aSTiwei Bie return -EINVAL;
284*a0e2cb6aSTiwei Bie
285*a0e2cb6aSTiwei Bie if (pread(dev->device, buf, size, dev->region[index].offset + offset) < 0)
286*a0e2cb6aSTiwei Bie return -errno;
287*a0e2cb6aSTiwei Bie
288*a0e2cb6aSTiwei Bie return 0;
289*a0e2cb6aSTiwei Bie }
290*a0e2cb6aSTiwei Bie
vfio_region_write(struct uml_vfio_user_device * dev,unsigned int index,uint64_t offset,const void * buf,uint64_t size)291*a0e2cb6aSTiwei Bie static int vfio_region_write(struct uml_vfio_user_device *dev, unsigned int index,
292*a0e2cb6aSTiwei Bie uint64_t offset, const void *buf, uint64_t size)
293*a0e2cb6aSTiwei Bie {
294*a0e2cb6aSTiwei Bie if (index >= dev->num_regions || offset + size > dev->region[index].size)
295*a0e2cb6aSTiwei Bie return -EINVAL;
296*a0e2cb6aSTiwei Bie
297*a0e2cb6aSTiwei Bie if (pwrite(dev->device, buf, size, dev->region[index].offset + offset) < 0)
298*a0e2cb6aSTiwei Bie return -errno;
299*a0e2cb6aSTiwei Bie
300*a0e2cb6aSTiwei Bie return 0;
301*a0e2cb6aSTiwei Bie }
302*a0e2cb6aSTiwei Bie
uml_vfio_user_cfgspace_read(struct uml_vfio_user_device * dev,unsigned int offset,void * buf,int size)303*a0e2cb6aSTiwei Bie int uml_vfio_user_cfgspace_read(struct uml_vfio_user_device *dev,
304*a0e2cb6aSTiwei Bie unsigned int offset, void *buf, int size)
305*a0e2cb6aSTiwei Bie {
306*a0e2cb6aSTiwei Bie return vfio_region_read(dev, VFIO_PCI_CONFIG_REGION_INDEX,
307*a0e2cb6aSTiwei Bie offset, buf, size);
308*a0e2cb6aSTiwei Bie }
309*a0e2cb6aSTiwei Bie
uml_vfio_user_cfgspace_write(struct uml_vfio_user_device * dev,unsigned int offset,const void * buf,int size)310*a0e2cb6aSTiwei Bie int uml_vfio_user_cfgspace_write(struct uml_vfio_user_device *dev,
311*a0e2cb6aSTiwei Bie unsigned int offset, const void *buf, int size)
312*a0e2cb6aSTiwei Bie {
313*a0e2cb6aSTiwei Bie return vfio_region_write(dev, VFIO_PCI_CONFIG_REGION_INDEX,
314*a0e2cb6aSTiwei Bie offset, buf, size);
315*a0e2cb6aSTiwei Bie }
316*a0e2cb6aSTiwei Bie
uml_vfio_user_bar_read(struct uml_vfio_user_device * dev,int bar,unsigned int offset,void * buf,int size)317*a0e2cb6aSTiwei Bie int uml_vfio_user_bar_read(struct uml_vfio_user_device *dev, int bar,
318*a0e2cb6aSTiwei Bie unsigned int offset, void *buf, int size)
319*a0e2cb6aSTiwei Bie {
320*a0e2cb6aSTiwei Bie return vfio_region_read(dev, bar, offset, buf, size);
321*a0e2cb6aSTiwei Bie }
322*a0e2cb6aSTiwei Bie
uml_vfio_user_bar_write(struct uml_vfio_user_device * dev,int bar,unsigned int offset,const void * buf,int size)323*a0e2cb6aSTiwei Bie int uml_vfio_user_bar_write(struct uml_vfio_user_device *dev, int bar,
324*a0e2cb6aSTiwei Bie unsigned int offset, const void *buf, int size)
325*a0e2cb6aSTiwei Bie {
326*a0e2cb6aSTiwei Bie return vfio_region_write(dev, bar, offset, buf, size);
327*a0e2cb6aSTiwei Bie }
328