Changeset 86726 in vbox for trunk/src/VBox/VMM/VMMR3
- Timestamp:
- Oct 28, 2020 10:10:29 AM (4 years ago)
- svn:sync-xref-src-repo-rev:
- 141114
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/VMM/VMMR3/DBGFR3Bp.cpp
r86704 r86726 157 157 #include <iprt/mem.h> 158 158 159 #include "DBGFInline.h" 160 159 161 160 162 /********************************************************************************************************************************* … … 855 857 856 858 /** 859 * Returns the pointer to the L2 table entry from the given index. 860 * 861 * @returns Current context pointer to the L2 table entry or NULL if the provided index value is invalid. 862 * @param pUVM The user mode VM handle. 863 * @param idxL2 The L2 table index to resolve. 864 * 865 * @note The content of the resolved L2 table entry is not validated!. 866 */ 867 DECLINLINE(PDBGFBPL2ENTRY) dbgfR3BpL2GetByIdx(PUVM pUVM, uint32_t idxL2) 868 { 869 uint32_t idChunk = DBGF_BP_L2_IDX_GET_CHUNK_ID(idxL2); 870 uint32_t idxEntry = DBGF_BP_L2_IDX_GET_ENTRY(idxL2); 871 872 AssertReturn(idChunk < DBGF_BP_L2_TBL_CHUNK_COUNT, NULL); 873 AssertReturn(idxEntry < DBGF_BP_L2_TBL_ENTRIES_PER_CHUNK, NULL); 874 875 PDBGFBPL2TBLCHUNKR3 pL2Chunk = &pUVM->dbgf.s.aBpL2TblChunks[idChunk]; 876 AssertPtrReturn(pL2Chunk->pbmAlloc, NULL); 877 AssertReturn(ASMBitTest(pL2Chunk->pbmAlloc, idxEntry), NULL); 878 879 return &pL2Chunk->CTX_SUFF(pL2Base)[idxEntry]; 880 } 881 882 883 /** 884 * Creates a binary search tree with the given root and leaf nodes. 885 * 886 * @returns VBox status code. 887 * @param pUVM The user mode VM handle. 888 * @param idxL1 The index into the L1 table where the created tree should be linked into. 889 * @param u32EntryOld The old entry in the L1 table used to compare with in the atomic update. 890 * @param hBpRoot The root node DBGF handle to assign. 891 * @param GCPtrRoot The root nodes GC pointer to use as a key. 892 * @param hBpLeaf The leafs node DBGF handle to assign. 893 * @param GCPtrLeaf The leafs node GC pointer to use as a key. 894 */ 895 static int dbgfR3BpInt3L2BstCreate(PUVM pUVM, uint32_t idxL1, uint32_t u32EntryOld, 896 DBGFBP hBpRoot, RTGCUINTPTR GCPtrRoot, 897 DBGFBP hBpLeaf, RTGCUINTPTR GCPtrLeaf) 898 { 899 AssertReturn(GCPtrRoot != GCPtrLeaf, VERR_DBGF_BP_IPE_9); 900 Assert(DBGF_BP_INT3_L1_IDX_EXTRACT_FROM_ADDR(GCPtrRoot) == DBGF_BP_INT3_L1_IDX_EXTRACT_FROM_ADDR(GCPtrLeaf)); 901 902 /* Allocate two nodes. */ 903 uint32_t idxL2Root = 0; 904 PDBGFBPL2ENTRY pL2Root = NULL; 905 int rc = dbgfR3BpL2TblEntryAlloc(pUVM, &idxL2Root, &pL2Root); 906 if (RT_SUCCESS(rc)) 907 { 908 uint32_t idxL2Leaf = 0; 909 PDBGFBPL2ENTRY pL2Leaf = NULL; 910 rc = dbgfR3BpL2TblEntryAlloc(pUVM, &idxL2Leaf, &pL2Leaf); 911 if (RT_SUCCESS(rc)) 912 { 913 dbgfBpL2TblEntryInit(pL2Leaf, hBpLeaf, GCPtrLeaf, DBGF_BP_L2_ENTRY_IDX_END, DBGF_BP_L2_ENTRY_IDX_END, 0 /*iDepth*/); 914 if (GCPtrLeaf < GCPtrRoot) 915 dbgfBpL2TblEntryInit(pL2Root, hBpRoot, GCPtrRoot, idxL2Leaf, DBGF_BP_L2_ENTRY_IDX_END, 0 /*iDepth*/); 916 else 917 dbgfBpL2TblEntryInit(pL2Root, hBpRoot, GCPtrRoot, DBGF_BP_L2_ENTRY_IDX_END, idxL2Leaf, 0 /*iDepth*/); 918 919 uint32_t const u32Entry = DBGF_BP_INT3_L1_ENTRY_CREATE_L2_IDX(idxL2Root); 920 if (ASMAtomicCmpXchgU32(&pUVM->dbgf.s.paBpLocL1R3[idxL1], u32Entry, u32EntryOld)) 921 return VINF_SUCCESS; 922 923 /* The L1 entry has changed due to another thread racing us during insertion, free nodes and try again. */ 924 rc = VINF_TRY_AGAIN; 925 dbgfR3BpL2TblEntryFree(pUVM, idxL2Leaf, pL2Leaf); 926 } 927 928 dbgfR3BpL2TblEntryFree(pUVM, idxL2Root, pL2Root); 929 } 930 931 return rc; 932 } 933 934 935 /** 936 * Inserts the given breakpoint handle into an existing binary search tree. 937 * 938 * @returns VBox status code. 939 * @param pUVM The user mode VM handle. 940 * @param idxL2Root The index of the tree root in the L2 table. 941 * @param hBpRoot The node DBGF handle to insert. 942 * @param GCPtrRoot The nodes GC pointer to use as a key. 943 */ 944 static int dbgfR3BpInt2L2BstNodeInsert(PUVM pUVM, uint32_t idxL2Root, DBGFBP hBp, RTGCUINTPTR GCPtr) 945 { 946 /* Allocate a new node first. */ 947 uint32_t idxL2Nd = 0; 948 PDBGFBPL2ENTRY pL2Nd = NULL; 949 int rc = dbgfR3BpL2TblEntryAlloc(pUVM, &idxL2Nd, &pL2Nd); 950 if (RT_SUCCESS(rc)) 951 { 952 /* Walk the tree and find the correct node to insert to. */ 953 PDBGFBPL2ENTRY pL2Entry = dbgfR3BpL2GetByIdx(pUVM, idxL2Root); 954 while (RT_LIKELY(pL2Entry)) 955 { 956 /* Make a copy of the entry. */ 957 DBGFBPL2ENTRY L2Entry; 958 L2Entry.u64GCPtrKeyAndBpHnd1 = ASMAtomicReadU64((volatile uint64_t *)&pL2Entry->u64GCPtrKeyAndBpHnd1); 959 L2Entry.u64LeftRightIdxDepthBpHnd2 = ASMAtomicReadU64((volatile uint64_t *)&pL2Entry->u64LeftRightIdxDepthBpHnd2); 960 961 RTGCUINTPTR GCPtrL2Entry = DBGF_BP_L2_ENTRY_GET_GCPTR(L2Entry.u64GCPtrKeyAndBpHnd1); 962 AssertBreak(GCPtr != GCPtrL2Entry); 963 964 /* Not found, get to the next level. */ 965 uint32_t idxL2Next = (GCPtr < GCPtrL2Entry) 966 ? DBGF_BP_L2_ENTRY_GET_IDX_LEFT(L2Entry.u64LeftRightIdxDepthBpHnd2) 967 : DBGF_BP_L2_ENTRY_GET_IDX_RIGHT(L2Entry.u64LeftRightIdxDepthBpHnd2); 968 if (idxL2Next == DBGF_BP_L2_ENTRY_IDX_END) 969 { 970 /* Insert the new node here. */ 971 dbgfBpL2TblEntryInit(pL2Nd, hBp, GCPtr, DBGF_BP_L2_ENTRY_IDX_END, DBGF_BP_L2_ENTRY_IDX_END, 0 /*iDepth*/); 972 if (GCPtr < GCPtrL2Entry) 973 dbgfBpL2TblEntryUpdateLeft(pL2Entry, idxL2Next, 0 /*iDepth*/); 974 else 975 dbgfBpL2TblEntryUpdateRight(pL2Entry, idxL2Next, 0 /*iDepth*/); 976 return VINF_SUCCESS; 977 } 978 979 pL2Entry = dbgfR3BpL2GetByIdx(pUVM, idxL2Next); 980 } 981 982 rc = VERR_DBGF_BP_L2_LOOKUP_FAILED; 983 dbgfR3BpL2TblEntryFree(pUVM, idxL2Nd, pL2Nd); 984 } 985 986 return rc; 987 } 988 989 990 /** 991 * Adds the given breakpoint handle keyed with the GC pointer to the proper L2 binary search tree 992 * possibly creating a new tree. 993 * 994 * @returns VBox status code. 995 * @param pUVM The user mode VM handle. 996 * @param idxL1 The index into the L1 table the breakpoint uses. 997 * @param hBp The breakpoint handle which is to be added. 998 * @param GCPtr The GC pointer the breakpoint is keyed with. 999 */ 1000 static int dbgfR3BpInt3L2BstNodeAdd(PUVM pUVM, uint32_t idxL1, DBGFBP hBp, RTGCUINTPTR GCPtr) 1001 { 1002 int rc = RTSemFastMutexRequest(pUVM->dbgf.s.hMtxBpL2Wr); AssertRC(rc); 1003 1004 uint32_t u32Entry = ASMAtomicReadU32(&pUVM->dbgf.s.paBpLocL1R3[idxL1]); /* Re-read, could get raced by a remove operation. */ 1005 uint8_t u8Type = DBGF_BP_INT3_L1_ENTRY_GET_TYPE(u32Entry); 1006 if (u8Type == DBGF_BP_INT3_L1_ENTRY_TYPE_BP_HND) 1007 { 1008 /* Create a new search tree, gather the necessary information first. */ 1009 DBGFBP hBp2 = DBGF_BP_INT3_L1_ENTRY_GET_BP_HND(u32Entry); 1010 PDBGFBPINT pBp2 = dbgfR3BpGetByHnd(pUVM, hBp2); 1011 AssertStmt(VALID_PTR(pBp2), rc = VERR_DBGF_BP_IPE_7); 1012 if (RT_SUCCESS(rc)) 1013 rc = dbgfR3BpInt3L2BstCreate(pUVM, idxL1, u32Entry, hBp, GCPtr, hBp2, pBp2->Pub.u.Int3.GCPtr); 1014 } 1015 else if (u8Type == DBGF_BP_INT3_L1_ENTRY_TYPE_L2_IDX) 1016 rc = dbgfR3BpInt2L2BstNodeInsert(pUVM, DBGF_BP_INT3_L1_ENTRY_GET_L2_IDX(u32Entry), hBp, GCPtr); 1017 1018 int rc2 = RTSemFastMutexRelease(pUVM->dbgf.s.hMtxBpL2Wr); AssertRC(rc2); 1019 return rc; 1020 } 1021 1022 1023 /** 1024 * Removes the given breakpoint handle keyed with the GC pointer from the L2 binary search tree 1025 * pointed to by the given L2 root index. 1026 * 1027 * @returns VBox status code. 1028 * @param pUVM The user mode VM handle. 1029 * @param idxL1 The index into the L1 table pointing to the binary search tree. 1030 * @param idxL2Root The L2 table index where the tree root is located. 1031 * @param hBp The breakpoint handle which is to be removed. 1032 * @param GCPtr The GC pointer the breakpoint is keyed with. 1033 */ 1034 static int dbgfR3BpInt2L2BstNodeRemove(PUVM pUVM, uint32_t idxL1, uint32_t idxL2Root, DBGFBP hBp, RTGCUINTPTR GCPtr) 1035 { 1036 int rc = RTSemFastMutexRequest(pUVM->dbgf.s.hMtxBpL2Wr); AssertRC(rc); 1037 1038 RT_NOREF(idxL1, idxL2Root, hBp, GCPtr); 1039 1040 int rc2 = RTSemFastMutexRelease(pUVM->dbgf.s.hMtxBpL2Wr); AssertRC(rc2); 1041 1042 return rc; 1043 } 1044 1045 1046 /** 857 1047 * Adds the given int3 breakpoint to the appropriate lookup tables. 858 1048 * … … 886 1076 else 887 1077 { 888 uint8_t u8Type = DBGF_BP_INT3_L1_ENTRY_GET_TYPE(u32Entry); 889 if (u8Type == DBGF_BP_INT3_L1_ENTRY_TYPE_BP_HND) 890 { 891 /** @todo Allocate a new root entry and one leaf to accomodate for the two handles, 892 * then replace the new entry. */ 893 rc = VERR_NOT_IMPLEMENTED; 894 break; 895 } 896 else if (u8Type == DBGF_BP_INT3_L1_ENTRY_TYPE_L2_IDX) 897 { 898 /** @todo Walk the L2 tree searching for the correct spot, add the new entry 899 * and rebalance the tree. */ 900 rc = VERR_NOT_IMPLEMENTED; 901 break; 902 } 1078 rc = dbgfR3BpInt3L2BstNodeAdd(pUVM, idxL1, hBp, pBp->Pub.u.Int3.GCPtr); 1079 if (rc == VINF_TRY_AGAIN) 1080 continue; 1081 1082 break; 903 1083 } 904 1084 } … … 913 1093 914 1094 /** 1095 * @callback_method_impl{FNVMMEMTRENDEZVOUS} 1096 */ 1097 static DECLCALLBACK(VBOXSTRICTRC) dbgfR3BpInt3RemoveEmtWorker(PVM pVM, PVMCPU pVCpu, void *pvUser) 1098 { 1099 DBGFBP hBp = (DBGFBP)(uintptr_t)pvUser; 1100 1101 VMCPU_ASSERT_EMT(pVCpu); 1102 VM_ASSERT_VALID_EXT_RETURN(pVM, VERR_INVALID_VM_HANDLE); 1103 1104 PUVM pUVM = pVM->pUVM; 1105 PDBGFBPINT pBp = dbgfR3BpGetByHnd(pUVM, hBp); 1106 AssertPtrReturn(pBp, VERR_DBGF_BP_IPE_8); 1107 1108 int rc = VINF_SUCCESS; 1109 if (pVCpu->idCpu == 0) 1110 { 1111 uint16_t idxL1 = DBGF_BP_INT3_L1_IDX_EXTRACT_FROM_ADDR(pBp->Pub.u.Int3.GCPtr); 1112 uint32_t u32Entry = ASMAtomicReadU32(&pUVM->dbgf.s.paBpLocL1R3[idxL1]); 1113 AssertReturn(u32Entry != DBGF_BP_INT3_L1_ENTRY_TYPE_NULL, VERR_DBGF_BP_IPE_6); 1114 1115 uint8_t u8Type = DBGF_BP_INT3_L1_ENTRY_GET_TYPE(u32Entry); 1116 if (u8Type == DBGF_BP_INT3_L1_ENTRY_TYPE_BP_HND) 1117 { 1118 /* Single breakpoint, just exchange atomically with the null value. */ 1119 if (!ASMAtomicCmpXchgU32(&pUVM->dbgf.s.paBpLocL1R3[idxL1], DBGF_BP_INT3_L1_ENTRY_TYPE_NULL, u32Entry)) 1120 { 1121 /* 1122 * A breakpoint addition must have raced us converting the L1 entry to an L2 index type, re-read 1123 * and remove the node from the created binary search tree. 1124 * 1125 * This works because after the entry was converted to an L2 index it can only be converted back 1126 * to a direct handle by removing one or more nodes which always goes through the fast mutex 1127 * protecting the L2 table. Likewise adding a new breakpoint requires grabbing the mutex as well 1128 * so there is serialization here and the node can be removed safely without having to worry about 1129 * concurrent tree modifications. 1130 */ 1131 u32Entry = ASMAtomicReadU32(&pUVM->dbgf.s.paBpLocL1R3[idxL1]); 1132 AssertReturn(DBGF_BP_INT3_L1_ENTRY_GET_TYPE(u32Entry) == DBGF_BP_INT3_L1_ENTRY_TYPE_L2_IDX, VERR_DBGF_BP_IPE_9); 1133 1134 rc = dbgfR3BpInt2L2BstNodeRemove(pUVM, idxL1, DBGF_BP_INT3_L1_ENTRY_GET_L2_IDX(u32Entry), 1135 hBp, pBp->Pub.u.Int3.GCPtr); 1136 } 1137 } 1138 else if (u8Type == DBGF_BP_INT3_L1_ENTRY_TYPE_L2_IDX) 1139 rc = dbgfR3BpInt2L2BstNodeRemove(pUVM, idxL1, DBGF_BP_INT3_L1_ENTRY_GET_L2_IDX(u32Entry), 1140 hBp, pBp->Pub.u.Int3.GCPtr); 1141 } 1142 1143 return rc; 1144 } 1145 1146 1147 /** 915 1148 * Removes the given int3 breakpoint from all lookup tables. 916 1149 * … … 924 1157 AssertReturn(DBGF_BP_PUB_GET_TYPE(pBp->Pub.fFlagsAndType) == DBGFBPTYPE_INT3, VERR_DBGF_BP_IPE_3); 925 1158 926 int rc = VINF_SUCCESS; 927 uint16_t idxL1 = DBGF_BP_INT3_L1_IDX_EXTRACT_FROM_ADDR(pBp->Pub.u.Int3.GCPtr); 928 929 for (;;) 930 { 931 uint32_t u32Entry = ASMAtomicReadU32(&pUVM->dbgf.s.paBpLocL1R3[idxL1]); 932 AssertReturn(u32Entry != DBGF_BP_INT3_L1_ENTRY_TYPE_NULL, VERR_DBGF_BP_IPE_6); 933 934 uint8_t u8Type = DBGF_BP_INT3_L1_ENTRY_GET_TYPE(u32Entry); 935 if (u8Type == DBGF_BP_INT3_L1_ENTRY_TYPE_BP_HND) 936 { 937 /* Single breakpoint, just exchange atomically with the null value. */ 938 if (ASMAtomicCmpXchgU32(&pUVM->dbgf.s.paBpLocL1R3[idxL1], DBGF_BP_INT3_L1_ENTRY_TYPE_NULL, u32Entry)) 939 break; 940 break; 941 } 942 else if (u8Type == DBGF_BP_INT3_L1_ENTRY_TYPE_L2_IDX) 943 { 944 /** @todo Walk the L2 tree searching for the correct spot, remove the entry 945 * and rebalance the tree. */ 946 RT_NOREF(hBp); 947 rc = VERR_NOT_IMPLEMENTED; 948 break; 949 } 950 } 951 952 return rc; 1159 /* 1160 * This has to be done by an EMT rendezvous in order to not have an EMT traversing 1161 * any L2 trees while it is being removed. 1162 */ 1163 return VMMR3EmtRendezvous(pUVM->pVM, VMMEMTRENDEZVOUS_FLAGS_TYPE_ALL_AT_ONCE, dbgfR3BpInt3RemoveEmtWorker, (void *)(uintptr_t)hBp); 953 1164 } 954 1165
Note:
See TracChangeset
for help on using the changeset viewer.