@@ -3,11 +3,32 @@ use crate::memory;
33use crate :: virtdev:: {
44 DeviceEvent , MemReadRequest , MemWriteRequest , ResponseEventArray ,
55} ;
6- use crate :: { vcpu, vmcs, vmexit} ;
6+ use crate :: { vcpu, vm , vmcs, vmexit} ;
77use arrayvec:: ArrayVec ;
88use byteorder:: ByteOrder ;
99use core:: mem:: size_of;
1010use iced_x86;
11+ use x86:: bits64:: paging:: BASE_PAGE_SIZE ;
12+
13+ trait MemIoCallback :
14+ Fn (
15+ & mut vcpu:: VCpu ,
16+ memory:: GuestPhysAddr ,
17+ DeviceEvent ,
18+ & mut ResponseEventArray ,
19+ ) -> Result < ( ) >
20+ {
21+ }
22+
23+ impl < T > MemIoCallback for T where
24+ T : Fn (
25+ & mut vcpu:: VCpu ,
26+ memory:: GuestPhysAddr ,
27+ DeviceEvent ,
28+ & mut ResponseEventArray ,
29+ ) -> Result < ( ) >
30+ {
31+ }
1132
1233macro_rules! read_register {
1334 ( $out: ident, $value: expr, $type: ty) => { {
@@ -138,6 +159,7 @@ fn do_mmio_write(
138159 guest_cpu : & mut vmexit:: GuestCpuState ,
139160 responses : & mut ResponseEventArray ,
140161 instr : iced_x86:: Instruction ,
162+ on_write : impl MemIoCallback ,
141163) -> Result < ( ) > {
142164 let mut res = ArrayVec :: < [ u8 ; 8 ] > :: new ( ) ;
143165 let data = match instr. op1_kind ( ) {
@@ -169,11 +191,10 @@ fn do_mmio_write(
169191 } ;
170192 let request = MemWriteRequest :: new ( & data[ ..] ) ;
171193
172- let mut vm = vcpu . vm . write ( ) ;
173- vm . dispatch_event (
194+ ( on_write ) (
195+ vcpu ,
174196 addr,
175197 crate :: virtdev:: DeviceEvent :: MemWrite ( addr, request) ,
176- vcpu,
177198 responses,
178199 )
179200}
@@ -184,6 +205,7 @@ fn do_mmio_read(
184205 guest_cpu : & mut vmexit:: GuestCpuState ,
185206 responses : & mut ResponseEventArray ,
186207 instr : iced_x86:: Instruction ,
208+ on_read : impl MemIoCallback ,
187209) -> Result < ( ) > {
188210 let ( reg, size, offset) = match instr. op0_kind ( ) {
189211 iced_x86:: OpKind :: Register => match instr. op_register ( 0 ) {
@@ -341,13 +363,7 @@ fn do_mmio_read(
341363
342364 let mut arr = [ 0u8 ; size_of :: < u64 > ( ) ] ;
343365 let request = MemReadRequest :: new ( & mut arr[ ..size] ) ;
344- let mut vm = vcpu. vm . write ( ) ;
345- vm. dispatch_event (
346- addr,
347- DeviceEvent :: MemRead ( addr, request) ,
348- vcpu,
349- responses,
350- ) ?;
366+ ( on_read) ( vcpu, addr, DeviceEvent :: MemRead ( addr, request) , responses) ?;
351367
352368 let ( value, mask) = match size {
353369 1 => ( arr[ 0 ] as u64 , ( ( u8:: MAX as u64 ) << ( offset * 8 ) ) ) ,
@@ -372,11 +388,13 @@ fn do_mmio_read(
372388 Ok ( ( ) )
373389}
374390
375- pub fn handle_ept_violation (
391+ fn process_memio_op (
392+ addr : memory:: GuestPhysAddr ,
376393 vcpu : & mut vcpu:: VCpu ,
377394 guest_cpu : & mut vmexit:: GuestCpuState ,
378- _exit : vmexit:: EptInformation ,
379395 responses : & mut ResponseEventArray ,
396+ on_read : impl MemIoCallback ,
397+ on_write : impl MemIoCallback ,
380398) -> Result < ( ) > {
381399 let instruction_len = vcpu
382400 . vmcs
@@ -406,21 +424,16 @@ pub fn handle_ept_violation(
406424 decoder. set_ip ( ip) ;
407425 let instr = decoder. decode ( ) ;
408426
409- let addr = memory:: GuestPhysAddr :: new (
410- vcpu. vmcs
411- . read_field ( vmcs:: VmcsField :: GuestPhysicalAddress ) ?,
412- ) ;
413-
414427 // For now, just assume everything is like MOV. This is obviously very
415428 // incomplete.
416429 if instr. op0_kind ( ) == iced_x86:: OpKind :: Memory
417430 || instr. op0_kind ( ) == iced_x86:: OpKind :: Memory64
418431 {
419- do_mmio_write ( addr, vcpu, guest_cpu, responses, instr) ?;
432+ do_mmio_write ( addr, vcpu, guest_cpu, responses, instr, on_write ) ?;
420433 } else if instr. op1_kind ( ) == iced_x86:: OpKind :: Memory
421434 || instr. op1_kind ( ) == iced_x86:: OpKind :: Memory64
422435 {
423- do_mmio_read ( addr, vcpu, guest_cpu, responses, instr) ?;
436+ do_mmio_read ( addr, vcpu, guest_cpu, responses, instr, on_read ) ?;
424437 } else {
425438 return Err ( Error :: InvalidValue ( format ! (
426439 "Unsupported mmio instruction: {:?} (rip=0x{:x}, bytes={:?})" ,
@@ -429,6 +442,103 @@ pub fn handle_ept_violation(
429442 bytes,
430443 ) ) ) ;
431444 }
432-
433445 Ok ( ( ) )
434446}
447+
448+ pub fn handle_ept_violation (
449+ vcpu : & mut vcpu:: VCpu ,
450+ guest_cpu : & mut vmexit:: GuestCpuState ,
451+ _exit : vmexit:: EptInformation ,
452+ responses : & mut ResponseEventArray ,
453+ ) -> Result < ( ) > {
454+ fn on_ept_violation (
455+ vcpu : & mut vcpu:: VCpu ,
456+ addr : memory:: GuestPhysAddr ,
457+ event : DeviceEvent ,
458+ responses : & mut ResponseEventArray ,
459+ ) -> Result < ( ) > {
460+ let mut vm = vcpu. vm . write ( ) ;
461+ vm. dispatch_event ( addr, event, vcpu, responses)
462+ }
463+
464+ let addr = memory:: GuestPhysAddr :: new (
465+ vcpu. vmcs
466+ . read_field ( vmcs:: VmcsField :: GuestPhysicalAddress ) ?,
467+ ) ;
468+
469+ process_memio_op (
470+ addr,
471+ vcpu,
472+ guest_cpu,
473+ responses,
474+ on_ept_violation,
475+ on_ept_violation,
476+ )
477+ }
478+
479+ pub fn handle_apic_access (
480+ vcpu : & mut vcpu:: VCpu ,
481+ guest_cpu : & mut vmexit:: GuestCpuState ,
482+ exit : vmexit:: ApicAccessInformation ,
483+ responses : & mut ResponseEventArray ,
484+ ) -> Result < ( ) > {
485+ fn address_to_apic_offset ( addr : memory:: GuestPhysAddr ) -> u16 {
486+ let addr = addr. as_u64 ( ) ;
487+ let apic_base = vm:: GUEST_LOCAL_APIC_ADDR . as_u64 ( ) ;
488+ assert ! (
489+ addr >= apic_base && addr < ( apic_base + BASE_PAGE_SIZE as u64 )
490+ ) ;
491+ ( ( addr - apic_base) / size_of :: < u32 > ( ) as u64 ) as u16
492+ }
493+
494+ fn on_apic_read (
495+ vcpu : & mut vcpu:: VCpu ,
496+ addr : memory:: GuestPhysAddr ,
497+ event : DeviceEvent ,
498+ _responses : & mut ResponseEventArray ,
499+ ) -> Result < ( ) > {
500+ let offset = address_to_apic_offset ( addr) ;
501+ let res = vcpu. local_apic . register_read ( offset) ?;
502+ let mut bytes = res. to_be_bytes ( ) ;
503+
504+ match event {
505+ DeviceEvent :: MemRead ( _, mut req) => {
506+ req. as_mut_slice ( ) . copy_from_slice ( & mut bytes[ ..] ) ;
507+ Ok ( ( ) )
508+ }
509+ _ => return Err ( Error :: NotSupported ) ,
510+ }
511+ }
512+
513+ fn on_apic_write (
514+ vcpu : & mut vcpu:: VCpu ,
515+ addr : memory:: GuestPhysAddr ,
516+ event : DeviceEvent ,
517+ _responses : & mut ResponseEventArray ,
518+ ) -> Result < ( ) > {
519+ let offset = address_to_apic_offset ( addr) ;
520+ let mut bytes = [ 0u8 ; 4 ] ;
521+ let value = match event {
522+ DeviceEvent :: MemWrite ( _, req) => {
523+ bytes[ ..] . copy_from_slice ( req. as_slice ( ) ) ;
524+ u32:: from_be_bytes ( bytes)
525+ }
526+ _ => return Err ( Error :: NotSupported ) ,
527+ } ;
528+
529+ vcpu. local_apic . register_write ( offset, value)
530+ }
531+
532+ let addr = vm:: GUEST_LOCAL_APIC_ADDR
533+ + ( exit. offset . expect ( "Apic access with no offset" ) as usize
534+ * size_of :: < u32 > ( ) ) ;
535+
536+ process_memio_op (
537+ addr,
538+ vcpu,
539+ guest_cpu,
540+ responses,
541+ on_apic_read,
542+ on_apic_write,
543+ )
544+ }
0 commit comments