@@ -218,3 +218,217 @@ static enum es_result vc_insn_string_write(struct es_em_ctxt *ctxt,
218
218
219
219
return ret ;
220
220
}
221
+
222
+ #define IOIO_TYPE_STR BIT(2)
223
+ #define IOIO_TYPE_IN 1
224
+ #define IOIO_TYPE_INS (IOIO_TYPE_IN | IOIO_TYPE_STR)
225
+ #define IOIO_TYPE_OUT 0
226
+ #define IOIO_TYPE_OUTS (IOIO_TYPE_OUT | IOIO_TYPE_STR)
227
+
228
+ #define IOIO_REP BIT(3)
229
+
230
+ #define IOIO_ADDR_64 BIT(9)
231
+ #define IOIO_ADDR_32 BIT(8)
232
+ #define IOIO_ADDR_16 BIT(7)
233
+
234
+ #define IOIO_DATA_32 BIT(6)
235
+ #define IOIO_DATA_16 BIT(5)
236
+ #define IOIO_DATA_8 BIT(4)
237
+
238
+ #define IOIO_SEG_ES (0 << 10)
239
+ #define IOIO_SEG_DS (3 << 10)
240
+
241
+ static enum es_result vc_ioio_exitinfo (struct es_em_ctxt * ctxt , u64 * exitinfo )
242
+ {
243
+ struct insn * insn = & ctxt -> insn ;
244
+ * exitinfo = 0 ;
245
+
246
+ switch (insn -> opcode .bytes [0 ]) {
247
+ /* INS opcodes */
248
+ case 0x6c :
249
+ case 0x6d :
250
+ * exitinfo |= IOIO_TYPE_INS ;
251
+ * exitinfo |= IOIO_SEG_ES ;
252
+ * exitinfo |= (ctxt -> regs -> dx & 0xffff ) << 16 ;
253
+ break ;
254
+
255
+ /* OUTS opcodes */
256
+ case 0x6e :
257
+ case 0x6f :
258
+ * exitinfo |= IOIO_TYPE_OUTS ;
259
+ * exitinfo |= IOIO_SEG_DS ;
260
+ * exitinfo |= (ctxt -> regs -> dx & 0xffff ) << 16 ;
261
+ break ;
262
+
263
+ /* IN immediate opcodes */
264
+ case 0xe4 :
265
+ case 0xe5 :
266
+ * exitinfo |= IOIO_TYPE_IN ;
267
+ * exitinfo |= (u64 )insn -> immediate .value << 16 ;
268
+ break ;
269
+
270
+ /* OUT immediate opcodes */
271
+ case 0xe6 :
272
+ case 0xe7 :
273
+ * exitinfo |= IOIO_TYPE_OUT ;
274
+ * exitinfo |= (u64 )insn -> immediate .value << 16 ;
275
+ break ;
276
+
277
+ /* IN register opcodes */
278
+ case 0xec :
279
+ case 0xed :
280
+ * exitinfo |= IOIO_TYPE_IN ;
281
+ * exitinfo |= (ctxt -> regs -> dx & 0xffff ) << 16 ;
282
+ break ;
283
+
284
+ /* OUT register opcodes */
285
+ case 0xee :
286
+ case 0xef :
287
+ * exitinfo |= IOIO_TYPE_OUT ;
288
+ * exitinfo |= (ctxt -> regs -> dx & 0xffff ) << 16 ;
289
+ break ;
290
+
291
+ default :
292
+ return ES_DECODE_FAILED ;
293
+ }
294
+
295
+ switch (insn -> opcode .bytes [0 ]) {
296
+ case 0x6c :
297
+ case 0x6e :
298
+ case 0xe4 :
299
+ case 0xe6 :
300
+ case 0xec :
301
+ case 0xee :
302
+ /* Single byte opcodes */
303
+ * exitinfo |= IOIO_DATA_8 ;
304
+ break ;
305
+ default :
306
+ /* Length determined by instruction parsing */
307
+ * exitinfo |= (insn -> opnd_bytes == 2 ) ? IOIO_DATA_16
308
+ : IOIO_DATA_32 ;
309
+ }
310
+ switch (insn -> addr_bytes ) {
311
+ case 2 :
312
+ * exitinfo |= IOIO_ADDR_16 ;
313
+ break ;
314
+ case 4 :
315
+ * exitinfo |= IOIO_ADDR_32 ;
316
+ break ;
317
+ case 8 :
318
+ * exitinfo |= IOIO_ADDR_64 ;
319
+ break ;
320
+ }
321
+
322
+ if (insn_has_rep_prefix (insn ))
323
+ * exitinfo |= IOIO_REP ;
324
+
325
+ return ES_OK ;
326
+ }
327
+
328
+ static enum es_result vc_handle_ioio (struct ghcb * ghcb , struct es_em_ctxt * ctxt )
329
+ {
330
+ struct pt_regs * regs = ctxt -> regs ;
331
+ u64 exit_info_1 , exit_info_2 ;
332
+ enum es_result ret ;
333
+
334
+ ret = vc_ioio_exitinfo (ctxt , & exit_info_1 );
335
+ if (ret != ES_OK )
336
+ return ret ;
337
+
338
+ if (exit_info_1 & IOIO_TYPE_STR ) {
339
+
340
+ /* (REP) INS/OUTS */
341
+
342
+ bool df = ((regs -> flags & X86_EFLAGS_DF ) == X86_EFLAGS_DF );
343
+ unsigned int io_bytes , exit_bytes ;
344
+ unsigned int ghcb_count , op_count ;
345
+ unsigned long es_base ;
346
+ u64 sw_scratch ;
347
+
348
+ /*
349
+ * For the string variants with rep prefix the amount of in/out
350
+ * operations per #VC exception is limited so that the kernel
351
+ * has a chance to take interrupts and re-schedule while the
352
+ * instruction is emulated.
353
+ */
354
+ io_bytes = (exit_info_1 >> 4 ) & 0x7 ;
355
+ ghcb_count = sizeof (ghcb -> shared_buffer ) / io_bytes ;
356
+
357
+ op_count = (exit_info_1 & IOIO_REP ) ? regs -> cx : 1 ;
358
+ exit_info_2 = min (op_count , ghcb_count );
359
+ exit_bytes = exit_info_2 * io_bytes ;
360
+
361
+ es_base = insn_get_seg_base (ctxt -> regs , INAT_SEG_REG_ES );
362
+
363
+ /* Read bytes of OUTS into the shared buffer */
364
+ if (!(exit_info_1 & IOIO_TYPE_IN )) {
365
+ ret = vc_insn_string_read (ctxt ,
366
+ (void * )(es_base + regs -> si ),
367
+ ghcb -> shared_buffer , io_bytes ,
368
+ exit_info_2 , df );
369
+ if (ret )
370
+ return ret ;
371
+ }
372
+
373
+ /*
374
+ * Issue an VMGEXIT to the HV to consume the bytes from the
375
+ * shared buffer or to have it write them into the shared buffer
376
+ * depending on the instruction: OUTS or INS.
377
+ */
378
+ sw_scratch = __pa (ghcb ) + offsetof(struct ghcb , shared_buffer );
379
+ ghcb_set_sw_scratch (ghcb , sw_scratch );
380
+ ret = sev_es_ghcb_hv_call (ghcb , ctxt , SVM_EXIT_IOIO ,
381
+ exit_info_1 , exit_info_2 );
382
+ if (ret != ES_OK )
383
+ return ret ;
384
+
385
+ /* Read bytes from shared buffer into the guest's destination. */
386
+ if (exit_info_1 & IOIO_TYPE_IN ) {
387
+ ret = vc_insn_string_write (ctxt ,
388
+ (void * )(es_base + regs -> di ),
389
+ ghcb -> shared_buffer , io_bytes ,
390
+ exit_info_2 , df );
391
+ if (ret )
392
+ return ret ;
393
+
394
+ if (df )
395
+ regs -> di -= exit_bytes ;
396
+ else
397
+ regs -> di += exit_bytes ;
398
+ } else {
399
+ if (df )
400
+ regs -> si -= exit_bytes ;
401
+ else
402
+ regs -> si += exit_bytes ;
403
+ }
404
+
405
+ if (exit_info_1 & IOIO_REP )
406
+ regs -> cx -= exit_info_2 ;
407
+
408
+ ret = regs -> cx ? ES_RETRY : ES_OK ;
409
+
410
+ } else {
411
+
412
+ /* IN/OUT into/from rAX */
413
+
414
+ int bits = (exit_info_1 & 0x70 ) >> 1 ;
415
+ u64 rax = 0 ;
416
+
417
+ if (!(exit_info_1 & IOIO_TYPE_IN ))
418
+ rax = lower_bits (regs -> ax , bits );
419
+
420
+ ghcb_set_rax (ghcb , rax );
421
+
422
+ ret = sev_es_ghcb_hv_call (ghcb , ctxt , SVM_EXIT_IOIO , exit_info_1 , 0 );
423
+ if (ret != ES_OK )
424
+ return ret ;
425
+
426
+ if (exit_info_1 & IOIO_TYPE_IN ) {
427
+ if (!ghcb_rax_is_valid (ghcb ))
428
+ return ES_VMM_ERROR ;
429
+ regs -> ax = lower_bits (ghcb -> save .rax , bits );
430
+ }
431
+ }
432
+
433
+ return ret ;
434
+ }
0 commit comments