@@ -55,7 +55,9 @@ use {super::crashdump, std::path::Path};
55
55
56
56
use super :: fpu:: { FP_CONTROL_WORD_DEFAULT , FP_TAG_WORD_DEFAULT , MXCSR_DEFAULT } ;
57
57
#[ cfg( gdb) ]
58
- use super :: gdb:: { DebugCommChannel , DebugMsg , DebugResponse , GuestDebug , MshvDebug } ;
58
+ use super :: gdb:: {
59
+ DebugCommChannel , DebugMsg , DebugResponse , GuestDebug , MshvDebug , VcpuStopReason ,
60
+ } ;
59
61
#[ cfg( gdb) ]
60
62
use super :: handlers:: DbgMemAccessHandlerWrapper ;
61
63
use super :: handlers:: { MemAccessHandlerWrapper , OutBHandlerWrapper } ;
@@ -749,6 +751,25 @@ impl Hypervisor for HypervLinuxDriver {
749
751
. store ( false , Ordering :: Relaxed ) ;
750
752
HyperlightExit :: Cancelled ( )
751
753
} else {
754
+ // In case of the gdb feature, if no cancellation was requested,
755
+ // and the debugging is enabled it means the vCPU was stopped because
756
+ // of an interrupt coming from the debugger thread
757
+ #[ cfg( gdb) ]
758
+ if self . debug . is_some ( ) {
759
+ // If the vCPU was stopped because of an interrupt, we need to
760
+ // return a special exit reason so that the gdb thread can handle it
761
+ // and resume execution
762
+ // NOTE: There is a chance that the vCPU was stopped because of a stale
763
+ // signal that was meant to be delivered to a previous/other vCPU on this
764
+ // same thread, however, we cannot distinguish between the two cases, so
765
+ // we assume that the vCPU was stopped because of an interrupt.
766
+ // This is fine, because the debugger will be notified about an interrupt
767
+ HyperlightExit :: Debug ( VcpuStopReason :: Interrupt )
768
+ } else {
769
+ HyperlightExit :: Retry ( )
770
+ }
771
+
772
+ #[ cfg( not( gdb) ) ]
752
773
HyperlightExit :: Retry ( )
753
774
}
754
775
}
@@ -835,39 +856,130 @@ impl Hypervisor for HypervLinuxDriver {
835
856
dbg_mem_access_fn : std:: sync:: Arc <
836
857
std:: sync:: Mutex < dyn super :: handlers:: DbgMemAccessHandlerCaller > ,
837
858
> ,
838
- stop_reason : super :: gdb :: VcpuStopReason ,
859
+ stop_reason : VcpuStopReason ,
839
860
) -> Result < ( ) > {
840
- self . send_dbg_msg ( DebugResponse :: VcpuStopped ( stop_reason) )
841
- . map_err ( |e| new_error ! ( "Couldn't signal vCPU stopped event to GDB thread: {:?}" , e) ) ?;
861
+ if self . debug . is_none ( ) {
862
+ return Err ( new_error ! ( "Debugging is not enabled" ) ) ;
863
+ }
842
864
843
- loop {
844
- log:: debug!( "Debug wait for event to resume vCPU" ) ;
865
+ match stop_reason {
866
+ // If the vCPU stopped because of a crash, we need to handle it differently
867
+ // We do not want to allow resuming execution or placing breakpoints
868
+ // because the guest has crashed.
869
+ // We only allow reading registers and memory
870
+ VcpuStopReason :: Crash => {
871
+ self . send_dbg_msg ( DebugResponse :: VcpuStopped ( stop_reason) )
872
+ . map_err ( |e| {
873
+ new_error ! ( "Couldn't signal vCPU stopped event to GDB thread: {:?}" , e)
874
+ } ) ?;
875
+
876
+ loop {
877
+ log:: debug!( "Debug wait for event to resume vCPU" ) ;
878
+ // Wait for a message from gdb
879
+ let req = self . recv_dbg_msg ( ) ?;
880
+
881
+ // Flag to store if we should deny continue or step requests
882
+ let mut deny_continue = false ;
883
+ // Flag to store if we should detach from the gdb session
884
+ let mut detach = false ;
885
+
886
+ let response = match req {
887
+ // Allow the detach request to disable debugging by continuing resuming
888
+ // hypervisor crash error reporting
889
+ DebugMsg :: DisableDebug => {
890
+ detach = true ;
891
+ DebugResponse :: DisableDebug
892
+ }
893
+ // Do not allow continue or step requests
894
+ DebugMsg :: Continue | DebugMsg :: Step => {
895
+ deny_continue = true ;
896
+ DebugResponse :: NotAllowed
897
+ }
898
+ // Do not allow adding/removing breakpoints and writing to memory or registers
899
+ DebugMsg :: AddHwBreakpoint ( _)
900
+ | DebugMsg :: AddSwBreakpoint ( _)
901
+ | DebugMsg :: RemoveHwBreakpoint ( _)
902
+ | DebugMsg :: RemoveSwBreakpoint ( _)
903
+ | DebugMsg :: WriteAddr ( _, _)
904
+ | DebugMsg :: WriteRegisters ( _) => DebugResponse :: NotAllowed ,
905
+
906
+ // For all other requests, we will process them normally
907
+ _ => {
908
+ let result = self . process_dbg_request ( req, dbg_mem_access_fn. clone ( ) ) ;
909
+ match result {
910
+ Ok ( response) => response,
911
+ Err ( HyperlightError :: TranslateGuestAddress ( _) ) => {
912
+ // Treat non fatal errors separately so the guest doesn't fail
913
+ DebugResponse :: ErrorOccurred
914
+ }
915
+ Err ( e) => {
916
+ log:: error!( "Error processing debug request: {:?}" , e) ;
917
+ return Err ( e) ;
918
+ }
919
+ }
920
+ }
921
+ } ;
845
922
846
- // Wait for a message from gdb
847
- let req = self . recv_dbg_msg ( ) ?;
923
+ // Send the response to the request back to gdb
924
+ self . send_dbg_msg ( response)
925
+ . map_err ( |e| new_error ! ( "Couldn't send response to gdb: {:?}" , e) ) ?;
848
926
849
- let result = self . process_dbg_request ( req, dbg_mem_access_fn. clone ( ) ) ;
927
+ // If we are denying continue or step requests, the debugger assumes the
928
+ // execution started so we need to report a stop reason as a crash and let
929
+ // it request to read registers/memory to figure out what happened
930
+ if deny_continue {
931
+ self . send_dbg_msg ( DebugResponse :: VcpuStopped ( VcpuStopReason :: Crash ) )
932
+ . map_err ( |e| new_error ! ( "Couldn't send response to gdb: {:?}" , e) ) ?;
933
+ }
850
934
851
- let response = match result {
852
- Ok ( response) => response,
853
- // Treat non fatal errors separately so the guest doesn't fail
854
- Err ( HyperlightError :: TranslateGuestAddress ( _) ) => DebugResponse :: ErrorOccurred ,
855
- Err ( e) => {
856
- return Err ( e) ;
935
+ // If we are detaching, we will break the loop and the Hypervisor will continue
936
+ // to handle the Crash reason
937
+ if detach {
938
+ break ;
939
+ }
857
940
}
858
- } ;
941
+ }
942
+ // If the vCPU stopped because of any other reason except a crash, we can handle it
943
+ // normally
944
+ _ => {
945
+ // Send the stop reason to the gdb thread
946
+ self . send_dbg_msg ( DebugResponse :: VcpuStopped ( stop_reason) )
947
+ . map_err ( |e| {
948
+ new_error ! ( "Couldn't signal vCPU stopped event to GDB thread: {:?}" , e)
949
+ } ) ?;
950
+
951
+ loop {
952
+ log:: debug!( "Debug wait for event to resume vCPU" ) ;
953
+ // Wait for a message from gdb
954
+ let req = self . recv_dbg_msg ( ) ?;
955
+
956
+ let result = self . process_dbg_request ( req, dbg_mem_access_fn. clone ( ) ) ;
957
+
958
+ let response = match result {
959
+ Ok ( response) => response,
960
+ // Treat non fatal errors separately so the guest doesn't fail
961
+ Err ( HyperlightError :: TranslateGuestAddress ( _) ) => {
962
+ DebugResponse :: ErrorOccurred
963
+ }
964
+ Err ( e) => {
965
+ return Err ( e) ;
966
+ }
967
+ } ;
859
968
860
- // If the command was either step or continue, we need to run the vcpu
861
- let cont = matches ! (
862
- response,
863
- DebugResponse :: Step | DebugResponse :: Continue | DebugResponse :: DisableDebug
864
- ) ;
969
+ let cont = matches ! (
970
+ response,
971
+ DebugResponse :: Continue | DebugResponse :: Step | DebugResponse :: DisableDebug
972
+ ) ;
865
973
866
- self . send_dbg_msg ( response)
867
- . map_err ( |e| new_error ! ( "Couldn't send response to gdb: {:?}" , e) ) ?;
974
+ self . send_dbg_msg ( response)
975
+ . map_err ( |e| new_error ! ( "Couldn't send response to gdb: {:?}" , e) ) ?;
868
976
869
- if cont {
870
- break ;
977
+ // Check if we should continue execution
978
+ // We continue if the response is one of the following: Step, Continue, or DisableDebug
979
+ if cont {
980
+ break ;
981
+ }
982
+ }
871
983
}
872
984
}
873
985
0 commit comments