@@ -264,10 +264,27 @@ is_valid_region_access(vfu_ctx_t *vfu_ctx, size_t size, uint16_t cmd,
264
264
return false;
265
265
}
266
266
267
- if (cmd == VFIO_USER_REGION_WRITE && size - sizeof (* ra ) != ra -> count ) {
268
- vfu_log (vfu_ctx , LOG_ERR , "region write count too small: "
269
- "expected %lu, got %u" , size - sizeof (* ra ), ra -> count );
270
- return false;
267
+ switch (cmd ) {
268
+ case VFIO_USER_REGION_WRITE :
269
+ if (size - sizeof (* ra ) != ra -> count ) {
270
+ vfu_log (vfu_ctx , LOG_ERR , "region write count too small: "
271
+ "expected %lu, got %u" , size - sizeof (* ra ), ra -> count );
272
+ return false;
273
+ }
274
+
275
+ break ;
276
+
277
+ case VFIO_USER_REGION_WRITE_MULTI :
278
+ if (ra -> count > VFIO_USER_MULTI_DATA ) {
279
+ vfu_log (vfu_ctx , LOG_ERR , "region write count too large: "
280
+ "expected %lu, got %u" , size - sizeof (* ra ), ra -> count );
281
+ return false;
282
+ }
283
+
284
+ break ;
285
+
286
+ default :
287
+ break ;
271
288
}
272
289
273
290
index = ra -> region ;
@@ -350,6 +367,67 @@ handle_region_access(vfu_ctx_t *vfu_ctx, vfu_msg_t *msg)
350
367
return 0 ;
351
368
}
352
369
370
+ static int
371
+ handle_region_write_multi (vfu_ctx_t * vfu_ctx , vfu_msg_t * msg )
372
+ {
373
+ struct vfio_user_write_multi * wm = msg -> in .iov .iov_base ;
374
+ ssize_t ret ;
375
+ char * buf ;
376
+
377
+ assert (vfu_ctx != NULL );
378
+ assert (msg != NULL );
379
+
380
+ // FIXME: validate properly (?) - need tests
381
+
382
+ if (msg -> in .iov .iov_len < sizeof (wm -> wr_cnt )) {
383
+ return ERROR_INT (EINVAL );
384
+ }
385
+
386
+ if (wm -> wr_cnt > VFIO_USER_MULTI_MAX ) {
387
+ return ERROR_INT (EINVAL );
388
+ }
389
+
390
+ if (msg -> in .iov .iov_len !=
391
+ (offsetof(struct vfio_user_write_multi , wrs ) +
392
+ wm -> wr_cnt * sizeof (struct vfio_user_write_multi_data ))) {
393
+ return ERROR_INT (EINVAL );
394
+ }
395
+
396
+ for (size_t i = 0 ; i < wm -> wr_cnt ; i ++ ) {
397
+ struct vfio_user_region_access * in_ra ;
398
+
399
+ /* Re-use the very similar type. */
400
+ in_ra = (struct vfio_user_region_access * )& wm -> wrs [i ];
401
+
402
+ /*
403
+ * We already checked total length so can be sure each entry is at least
404
+ * big enough.
405
+ */
406
+ if (!is_valid_region_access (vfu_ctx , sizeof (wm -> wrs [i ]),
407
+ msg -> hdr .cmd , in_ra )) {
408
+ return ERROR_INT (EINVAL );
409
+ }
410
+
411
+ if (in_ra -> count == 0 ) {
412
+ continue ;
413
+ }
414
+
415
+ buf = (char * )(& in_ra -> data );
416
+
417
+ ret = region_access (vfu_ctx , in_ra -> region , buf , in_ra -> count ,
418
+ in_ra -> offset , true);
419
+ if (ret != in_ra -> count ) {
420
+ /* FIXME we should return whatever has been accessed, not an error */
421
+ if (ret >= 0 ) {
422
+ ret = ERROR_INT (EINVAL );
423
+ }
424
+ return ret ;
425
+ }
426
+ }
427
+
428
+ return 0 ;
429
+ }
430
+
353
431
static int
354
432
handle_device_get_info (vfu_ctx_t * vfu_ctx , vfu_msg_t * msg )
355
433
{
@@ -1164,6 +1242,11 @@ handle_request(vfu_ctx_t *vfu_ctx, vfu_msg_t *msg)
1164
1242
ret = handle_region_access (vfu_ctx , msg );
1165
1243
break ;
1166
1244
1245
+ case VFIO_USER_REGION_WRITE_MULTI :
1246
+ ret = handle_region_write_multi (vfu_ctx , msg );
1247
+ break ;
1248
+
1249
+
1167
1250
case VFIO_USER_DEVICE_RESET :
1168
1251
vfu_log (vfu_ctx , LOG_INFO , "device reset by client" );
1169
1252
ret = handle_device_reset (vfu_ctx , VFU_RESET_DEVICE );
@@ -1352,6 +1435,10 @@ command_needs_quiesce(vfu_ctx_t *vfu_ctx, const vfu_msg_t *msg)
1352
1435
return true;
1353
1436
}
1354
1437
break ;
1438
+
1439
+ case VFIO_USER_REGION_WRITE_MULTI :
1440
+ /* FIXME */
1441
+ return false;
1355
1442
}
1356
1443
1357
1444
return false;
0 commit comments