@@ -218,3 +218,217 @@ static enum es_result vc_insn_string_write(struct es_em_ctxt *ctxt,
218218
219219 return ret ;
220220}
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