@@ -55,7 +55,9 @@ use {super::crashdump, std::path::Path};
5555
5656use super :: fpu:: { FP_CONTROL_WORD_DEFAULT , FP_TAG_WORD_DEFAULT , MXCSR_DEFAULT } ;
5757#[ cfg( gdb) ]
58- use super :: gdb:: { DebugCommChannel , DebugMsg , DebugResponse , GuestDebug , MshvDebug } ;
58+ use super :: gdb:: {
59+ DebugCommChannel , DebugMsg , DebugResponse , GuestDebug , MshvDebug , VcpuStopReason ,
60+ } ;
5961#[ cfg( gdb) ]
6062use super :: handlers:: DbgMemAccessHandlerWrapper ;
6163use super :: handlers:: { MemAccessHandlerWrapper , OutBHandlerWrapper } ;
@@ -749,6 +751,25 @@ impl Hypervisor for HypervLinuxDriver {
749751 . store ( false , Ordering :: Relaxed ) ;
750752 HyperlightExit :: Cancelled ( )
751753 } 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) ) ]
752773 HyperlightExit :: Retry ( )
753774 }
754775 }
@@ -835,39 +856,130 @@ impl Hypervisor for HypervLinuxDriver {
835856 dbg_mem_access_fn : std:: sync:: Arc <
836857 std:: sync:: Mutex < dyn super :: handlers:: DbgMemAccessHandlerCaller > ,
837858 > ,
838- stop_reason : super :: gdb :: VcpuStopReason ,
859+ stop_reason : VcpuStopReason ,
839860 ) -> 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+ }
842864
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+ } ;
845922
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) ) ?;
848926
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+ }
850934
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+ }
857940 }
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+ } ;
859968
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+ ) ;
865973
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) ) ?;
868976
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+ }
871983 }
872984 }
873985
0 commit comments