@@ -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