Bitdefender Hypervisor Memory Introspection
icache.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2020 Bitdefender
3  * SPDX-License-Identifier: Apache-2.0
4  */
5 #include "icache.h"
6 #include "hook.h"
7 
8 
9 static __inline DWORD
11  _In_ PINS_CACHE Cache,
12  _In_ QWORD Gva
13  )
25 {
26  // We must include the entire page in this hash-table. Can't use bits lower than 12.
27  return (Gva >> 12) & (Cache->InvCount - 1);
28 }
29 
30 
31 static __inline DWORD
33  _In_ PINS_CACHE Cache,
34  _In_ QWORD Gva
35  )
47 {
48  // We use the frame number, so we can do fast invalidation on modified GVAs. Otherwise, we'd have to iterate
49  // the entire cache, which is very slow.
50  return (Gva >> 12) & (Cache->LinesCount - 1);
51 }
52 
53 
54 void
56  void
57  )
61 {
62  PINS_CACHE cache;
63  DWORD i, j;
64  LIST_ENTRY *list;
65 
67 
68  LOG("Instruction cache:\n");
69 
70  NLOG("Number of lines: %d\n", cache->LinesCount);
71  NLOG("Entries per line: %d\n", cache->EntriesCount);
72  NLOG("Invalidation size:%d\n", cache->InvCount);
73  NLOG("Fill count: %d\n", cache->FillRate);
74  NLOG("Flush count: %d\n", cache->FlushCount);
75  NLOG("Hit count: %d\n", cache->HitCount);
76  NLOG("Miss count: %d\n", cache->MissCount);
77  NLOG("Replace count: %d\n", cache->ReplaceCount);
78  NLOG("Pgflush count: %d\n", cache->PageFlushCount);
79 
80 
81  // Dump the entries inside the cache
82  for (i = 0; i < cache->LinesCount; i++)
83  {
84  for (j = 0; j < cache->EntriesCount; j++)
85  {
86  if (cache->Lines[i].Entries[j].Valid)
87  {
88  char text[ND_MIN_BUF_SIZE] = {0};
89 
90  NdToText(&cache->Lines[i].Entries[j].Instruction,
91  cache->Lines[i].Entries[j].Gva,
92  ND_MIN_BUF_SIZE,
93  text);
94 
95  NLOG("-> %04d - %04d: 0x%016llx:0x%016llx, %08d %d %d %d > %s\n",
96  i, j,
97  cache->Lines[i].Entries[j].Gva,
98  cache->Lines[i].Entries[j].Cr3,
99  cache->Lines[i].Entries[j].RefCount,
100  cache->Lines[i].Entries[j].Valid,
101  cache->Lines[i].Entries[j].Pinned,
102  cache->Lines[i].Entries[j].Global,
103  text);
104  }
105  }
106  }
107 
108  NLOG("Invalidation queue:\n");
109 
110  // Dump the invalidation queue
111  for (i = 0; i < cache->InvCount; i++)
112  {
113  if (IsListEmpty(&cache->InsInvGva[i]))
114  {
115  continue;
116  }
117 
118  NLOG("%04d: ", i);
119 
120  list = cache->InsInvGva[i].Flink;
121  while (list != &cache->InsInvGva[i])
122  {
124 
125  list = list->Flink;
126 
127  NLOG("0x%016llx:0x%016llx:0x%016llx:%04d ", pInv->Gva, pInv->Gpa, pInv->Cr3, pInv->RefCount);
128  }
129 
130  NLOG("\n");
131  }
132 }
133 
134 
135 static INTSTATUS
138  )
149 {
150  INTSTATUS status;
151 
152  status = INT_STATUS_SUCCESS;
153 
154  if (NULL != Invd->SwapHook)
155  {
156  status = IntHookPtsRemoveHook((HOOK_PTS **)&Invd->SwapHook, 0);
157  if (!INT_SUCCESS(status))
158  {
159  ERROR("[ERROR] IntHookPtsRemoveHook failed: 0x%08x\n", status);
160  }
161  }
162 
163  if (NULL != Invd->WriteHook)
164  {
165  status = IntHookGpaRemoveHook((HOOK_GPA **)&Invd->WriteHook, 0);
166  if (!INT_SUCCESS(status))
167  {
168  ERROR("[ERROR] IntHookGpaRemoveHook failed: 0x%08x\n", status);
169  }
170  }
171 
173 
174  return status;
175 }
176 
177 
178 static INTSTATUS
180  _In_ PINS_CACHE Cache
181  )
189 {
190  INTSTATUS status;
191  DWORD i;
192 
193  for (i = 0; i < Cache->InvCount; i++)
194  {
195  LIST_ENTRY *list = Cache->InsInvGva[i].Flink;
196 
197  while (list != &Cache->InsInvGva[i])
198  {
200  list = list->Flink;
201 
202  pInv->RefCount = 0;
203 
204  RemoveEntryList(&pInv->Link);
205 
206  status = IntIcFreeInvdEntry(pInv);
207  if (!INT_SUCCESS(status))
208  {
209  ERROR("[ERROR] IntIcFreeInvdEntry failed: 0x%08x\n", status);
210  }
211  }
212  }
213 
214  return INT_STATUS_SUCCESS;
215 }
216 
217 
218 static INTSTATUS
220  _In_ PINS_CACHE Cache,
222  )
232 {
233  INTSTATUS status;
234 
235  UNREFERENCED_PARAMETER(Cache);
236 
237  if (0 == Invd->RefCount)
238  {
240  }
241 
242  Invd->RefCount--;
243 
244  if (0 == Invd->RefCount)
245  {
246  RemoveEntryList(&Invd->Link);
247 
248  status = IntIcFreeInvdEntry(Invd);
249  if (!INT_SUCCESS(status))
250  {
251  ERROR("[ERROR] IntIcFreeInvdEntry failed: 0x%08x\n", status);
252  return status;
253  }
254  }
255 
256  return INT_STATUS_SUCCESS;
257 }
258 
259 
260 static INTSTATUS
262  _In_ PINS_CACHE Cache,
263  _In_ PINS_CACHE_ENTRY Entry
264  )
276 {
277  INTSTATUS status;
278 
279  if (NULL != Entry->Invd1)
280  {
281  status = IntIcInvdEntry(Cache, Entry->Invd1);
282  if (!INT_SUCCESS(status))
283  {
284  ERROR("[ERROR] IntIcInvdEntry failed: 0x%08x\n", status);
285  }
286 
287  Entry->Invd1 = NULL;
288  }
289 
290  if (NULL != Entry->Invd2)
291  {
292  status = IntIcInvdEntry(Cache, Entry->Invd2);
293  if (!INT_SUCCESS(status))
294  {
295  ERROR("[ERROR] IntIcInvdEntry failed: 0x%08x\n", status);
296  }
297 
298  Entry->Invd2 = NULL;
299  }
300 
301  return INT_STATUS_SUCCESS;
302 }
303 
304 
305 static INTSTATUS
307  _In_ INS_CACHE_INV_ENTRY const *Context,
308  _In_ void const *Hook,
309  _In_ QWORD Address,
310  _Out_ INTRO_ACTION *Action
311  )
327 {
328  PINS_CACHE pCache;
329  INS_CACHE_INV_ENTRY const *pInv;
330  QWORD gva, cr3;
331  BOOLEAN spill;
332 
333  UNREFERENCED_PARAMETER(Address);
335 
336  // Get the cache.
338  pInv = Context;
339 
340  gva = pInv->Gva;
341  cr3 = pInv->Cr3;
342  spill = pInv->Spill;
343 
344  // Note: calls to IntIcFlushGvaPage will remove the hook and will free the pInvd entry if the ref count reaches 0.
345  // We cannot safely access the pInvd entry after calling IntIcFlushGvaPage.
346 
347  // Flush the instructions contained inside this (virtual) page.
348  IntIcFlushGvaPage(pCache, gva, cr3, FALSE);
349 
350  if (spill)
351  {
352  // If this page contains spilled instructions, invalidate those too.
353  IntIcFlushGvaPage(pCache, gva - 0x1000, cr3, TRUE);
354  }
355 
356  // Obviously, we allow the write.
357  *Action = introGuestAllowed;
358 
359  return INT_STATUS_SUCCESS;
360 }
361 
362 
363 static INTSTATUS
365  _In_ void *Context,
366  _In_ QWORD VirtualAddress,
367  _In_ QWORD OldEntry,
368  _In_ QWORD NewEntry,
369  _In_ QWORD OldPageSize,
370  _In_ QWORD NewPageSize
371  )
388 {
389  PINS_CACHE pCache;
391  QWORD gva, cr3;
392  BOOLEAN spill;
393 
394  UNREFERENCED_PARAMETER(Context);
395  UNREFERENCED_PARAMETER(VirtualAddress);
396  UNREFERENCED_PARAMETER(OldPageSize);
397  UNREFERENCED_PARAMETER(NewPageSize);
398 
400  pInv = (PINS_CACHE_INV_ENTRY)Context;
401 
402  gva = pInv->Gva;
403  cr3 = pInv->Cr3;
404  spill = pInv->Spill;
405 
406  // Note: calls to IntIcFlushGvaPage will remove the hook and will free the pInvd entry if the ref count reaches 0.
407 
408 #define INV_MASK (~0x60ULL)
409 
410  if ((OldEntry & INV_MASK) != (NewEntry & INV_MASK))
411  {
412  // Flush the instructions contained inside this page.
413  IntIcFlushGvaPage(pCache, gva, cr3, FALSE);
414 
415  if (spill)
416  {
417  // If this page contains spilled instructions, invalidate those too.
418  IntIcFlushGvaPage(pCache, gva - 0x1000, cr3, TRUE);
419  }
420  }
421 
422  return INT_STATUS_SUCCESS;
423 }
424 
425 
426 static INTSTATUS
428  _In_ PINS_CACHE Cache,
429  _Out_ PINSTRUX Instrux,
430  _In_ QWORD Gva,
431  _In_ QWORD Cr3,
432  _In_ DWORD Lru
433  )
450 {
451  INTSTATUS status;
452  DWORD line, i;
453 
454  // This function is internal, there's no point on validating the parameters - they have already been
455  // validated by the caller.
456 
457  // Get the cache line for this address
458  line = IntIcHashLine(Cache, Gva);
459 
460  status = INT_STATUS_NOT_FOUND;
461 
462  // Check every set for the tag & valid bit
463  for (i = 0; i < Cache->EntriesCount; i++)
464  {
465  if ((Cache->Lines[line].Entries[i].Gva == Gva) &&
466  (Cache->Lines[line].Entries[i].Valid) &&
467  ((Cache->Lines[line].Entries[i].Cr3 == Cr3) ||
468  (IC_ANY_VAS == Cr3) ||
469  (Cache->Lines[line].Entries[i].Global)))
470  {
471  memcpy(Instrux, &Cache->Lines[line].Entries[i].Instruction, sizeof(INSTRUX));
472 
473  Cache->Lines[line].Entries[i].RefCount += Lru;
474 
475  status = INT_STATUS_SUCCESS;
476 
477  break;
478  }
479  }
480 
481  if (INT_STATUS_NOT_FOUND == status)
482  {
483  Cache->MissCount++;
484  }
485  else
486  {
487  Cache->HitCount++;
488  }
489 
490  return status;
491 }
492 
493 
494 INTSTATUS
496  _In_ PINS_CACHE Cache,
497  _Out_ PINSTRUX Instrux,
498  _In_ QWORD Gva,
499  _In_ QWORD Cr3
500  )
517 {
518  INTSTATUS status;
519 
520  if (Cache == NULL)
521  {
523  }
524 
525  if (Instrux == NULL)
526  {
528  }
529 
530  status = IntIcLookupInstructionInternal(Cache, Instrux, Gva, Cr3, 1);
531 
532  return status;
533 }
534 
535 
536 INTSTATUS
538  _In_ PINS_CACHE Cache
539  )
549 //
550 {
551  INTSTATUS status;
552  DWORD i, j;
553 
554  if (Cache == NULL)
555  {
557  }
558 
559  if (!Cache->Dirty)
560  {
561  return INT_STATUS_SUCCESS;
562  }
563 
564  for (i = 0; i < Cache->LinesCount; i++)
565  {
566  for (j = 0; j < Cache->EntriesCount; j++)
567  {
568  if (Cache->Lines[i].Entries[j].Valid)
569  {
570  Cache->Lines[i].Entries[j].Gva = 0;
571  Cache->Lines[i].Entries[j].Cr3 = 0;
572  Cache->Lines[i].Entries[j].Invd1 = NULL;
573  Cache->Lines[i].Entries[j].Invd2 = NULL;
574  Cache->Lines[i].Entries[j].Pinned = FALSE;
575  Cache->Lines[i].Entries[j].Global = FALSE;
576  Cache->Lines[i].Entries[j].RefCount = 0;
577  Cache->Lines[i].Entries[j].Valid = FALSE;
578  }
579  }
580  }
581 
582  status = IntIcRemoveAllInvdEntries(Cache);
583  if (!INT_SUCCESS(status))
584  {
585  ERROR("[ERROR] IntIcRemoveAllInvdEntries failed: 0x%08x\n", status);
586  }
587 
588  Cache->FillRate = 0;
589  Cache->FlushCount++;
590  Cache->Dirty = FALSE;
591 
592  return INT_STATUS_SUCCESS;
593 }
594 
595 
596 INTSTATUS
598  _In_ PINS_CACHE Cache,
599  _In_ QWORD Gva,
600  _In_ QWORD Cr3
601  )
621 {
622  INTSTATUS status;
623  DWORD line, i;
624 
625  if (Cache == NULL)
626  {
628  }
629 
630  line = IntIcHashLine(Cache, Gva);
631 
632  status = INT_STATUS_NOT_FOUND;
633 
634  for (i = 0; i < Cache->EntriesCount; i++)
635  {
636  if ((Cache->Lines[line].Entries[i].Gva == Gva) &&
637  (Cache->Lines[line].Entries[i].Valid) &&
638  ((Cache->Lines[line].Entries[i].Cr3 == Cr3) ||
639  (IC_ANY_VAS == Cr3) ||
640  (Cache->Lines[line].Entries[i].Global)))
641  {
642  // Invalidate the Invd entry.
643  status = IntIcInvdCacheEntry(Cache, &Cache->Lines[line].Entries[i]);
644  if (!INT_SUCCESS(status))
645  {
646  ERROR("[ERROR] IntIcInvdCacheEntry failed: 0x%08x\n", status);
647  }
648 
649  Cache->Lines[line].Entries[i].Valid = 0;
650  Cache->Lines[line].Entries[i].Gva = 0;
651  Cache->Lines[line].Entries[i].Cr3 = 0;
652  Cache->Lines[line].Entries[i].Invd1 = NULL;
653  Cache->Lines[line].Entries[i].Invd2 = NULL;
654 
655  status = INT_STATUS_SUCCESS;
656  }
657  }
658 
659  return status;
660 }
661 
662 
663 INTSTATUS
665  _In_ PINS_CACHE Cache,
666  _In_ QWORD Gva,
667  _In_ QWORD Cr3,
668  _In_ BOOLEAN Spill
669  )
686 {
687  INTSTATUS status;
688  DWORD line, i;
689 
690  if (Cache == NULL)
691  {
693  }
694 
695  Cache->PageFlushCount++;
696 
697  line = IntIcHashLine(Cache, Gva);
698 
699  Gva &= PAGE_MASK;
700  status = INT_STATUS_NOT_FOUND;
701 
702  for (i = 0; i < Cache->EntriesCount; i++)
703  {
704  if (((Cache->Lines[line].Entries[i].Gva & PAGE_MASK) == Gva) &&
705  (Cache->Lines[line].Entries[i].Valid) &&
706  ((Cache->Lines[line].Entries[i].Cr3 == Cr3) ||
707  (IC_ANY_VAS == Cr3) ||
708  (Cache->Lines[line].Entries[i].Global)) &&
709  ((!Spill) || ((Cache->Lines[line].Entries[i].Gva & PAGE_OFFSET) +
710  Cache->Lines[line].Entries[i].Instruction.Length > PAGE_SIZE)))
711  {
712  // Invalidate the Invd entry.
713  status = IntIcInvdCacheEntry(Cache, &Cache->Lines[line].Entries[i]);
714  if (!INT_SUCCESS(status))
715  {
716  ERROR("[ERROR] IntIcInvdCacheEntry failed: 0x%08x\n", status);
717  }
718 
719  Cache->Lines[line].Entries[i].Valid = 0;
720  Cache->Lines[line].Entries[i].Gva = 0;
721  Cache->Lines[line].Entries[i].Cr3 = 0;
722  Cache->Lines[line].Entries[i].Invd1 = NULL;
723  Cache->Lines[line].Entries[i].Invd2 = NULL;
724 
725  status = INT_STATUS_SUCCESS;
726  }
727  }
728 
729  return status;
730 }
731 
732 
733 INTSTATUS
735  _In_ PINS_CACHE Cache,
736  _In_ QWORD Gpa
737  )
751 {
752  INTSTATUS status;
753  DWORD i, j;
754 
755  if (Cache == NULL)
756  {
758  }
759 
760  status = INT_STATUS_NOT_FOUND;
761 
762  Gpa &= PHYS_PAGE_MASK;
763 
764  Cache->PageFlushCount++;
765 
766  for (i = 0; i < Cache->LinesCount; i++)
767  {
768  for (j = 0; j < Cache->EntriesCount; j++)
769  {
770  if ((Cache->Lines[i].Entries[j].Valid) &&
771  (((NULL != Cache->Lines[i].Entries[j].Invd1) && (Cache->Lines[i].Entries[j].Invd1->Gpa == Gpa)) ||
772  ((NULL != Cache->Lines[i].Entries[j].Invd2) && (Cache->Lines[i].Entries[j].Invd2->Gpa == Gpa))))
773  {
774  // Invalidate the Invd entry.
775  status = IntIcInvdCacheEntry(Cache, &Cache->Lines[i].Entries[j]);
776  if (!INT_SUCCESS(status))
777  {
778  ERROR("[ERROR] IntIcInvdCacheEntry failed: 0x%08x\n", status);
779  }
780 
781  Cache->Lines[i].Entries[j].Valid = 0;
782  Cache->Lines[i].Entries[j].Gva = 0;
783  Cache->Lines[i].Entries[j].Cr3 = 0;
784  Cache->Lines[i].Entries[j].Invd1 = NULL;
785  Cache->Lines[i].Entries[j].Invd2 = NULL;
786 
787  status = INT_STATUS_SUCCESS;
788  }
789  }
790  }
791 
792  return status;
793 }
794 
795 
796 INTSTATUS
798  _In_ PINS_CACHE Cache,
799  _In_ QWORD Cr3
800  )
812 {
813  INTSTATUS status;
814  DWORD i, j;
815 
816  if (Cache == NULL)
817  {
819  }
820 
821  status = INT_STATUS_SUCCESS;
822 
823  for (i = 0; i < Cache->LinesCount; i++)
824  {
825  for (j = 0; j < Cache->EntriesCount; j++)
826  {
827  if ((Cache->Lines[i].Entries[j].Cr3 == Cr3) || (IC_ANY_VAS == Cr3))
828  {
829  // Invalidate the Invd entry.
830  status = IntIcInvdCacheEntry(Cache, &Cache->Lines[i].Entries[j]);
831  if (!INT_SUCCESS(status))
832  {
833  ERROR("[ERROR] IntIcInvdCacheEntry failed: 0x%08x\n", status);
834  }
835 
836  Cache->Lines[i].Entries[j].Valid = 0;
837  Cache->Lines[i].Entries[j].Gva = 0;
838  Cache->Lines[i].Entries[j].Cr3 = 0;
839  Cache->Lines[i].Entries[j].Invd1 = NULL;
840  Cache->Lines[i].Entries[j].Invd2 = NULL;
841 
842  status = INT_STATUS_SUCCESS;
843  }
844  }
845  }
846 
847  return status;
848 }
849 
850 
851 static INTSTATUS
853  _In_ PINS_CACHE Cache,
854  _In_ QWORD Gva,
855  _In_ QWORD Cr3,
857  )
873 {
874  INTSTATUS status;
875  LIST_ENTRY *list;
876  DWORD invline;
877  BOOLEAN bFound;
879  VA_TRANSLATION tr = { 0 };
880 
881  bFound = FALSE;
882  pInv = NULL;
883 
884  invline = IntIcHashInv(Cache, Gva);
885 
886  // We first iterate the existing list of invalidation entries and simply increment the refcount of an existing
887  // entry, if available.
888  list = Cache->InsInvGva[invline].Flink;
889  while (list != &Cache->InsInvGva[invline])
890  {
891  pInv = CONTAINING_RECORD(list, INS_CACHE_INV_ENTRY, Link);
892  list = list->Flink;
893 
894  if ((pInv->Gva == (Gva & PAGE_MASK)) && (pInv->Cr3 == Cr3))
895  {
896  pInv->RefCount++;
897 
898  bFound = TRUE;
899 
900  break;
901  }
902  }
903 
904  // Invalidation entry not added yet - allocate it & add it now.
905  if (!bFound)
906  {
907  status = IntTranslateVirtualAddressEx(Gva & PAGE_MASK, Cr3 != 0 ? Cr3 : gGuest.Mm.SystemCr3, TRFLG_NONE, &tr);
908  if (!INT_SUCCESS(status))
909  {
910  return status;
911  }
912 
913  pInv = HpAllocWithTag(sizeof(*pInv), IC_TAG_IINV);
914  if (NULL == pInv)
915  {
917  }
918 
919  pInv->Cr3 = Cr3;
920  pInv->RefCount = 1;
921  pInv->Gva = Gva & PAGE_MASK;
922  pInv->Gpa = tr.PhysicalAddress & PHYS_PAGE_MASK;
923 
924  status = IntHookPtsSetHook(Cr3, pInv->Gva, IntIcSwapHandler, pInv, NULL, 0, (PHOOK_PTS *)&pInv->SwapHook);
925  if (!INT_SUCCESS(status))
926  {
927  ERROR("[ERROR] IntHookPtsSetHook failed: 0x%08x\n", status);
928  IntIcFreeInvdEntry(pInv);
929  return status;
930  }
931 
933  pInv, NULL, 0, (PHOOK_GPA *)&pInv->WriteHook);
934  if (!INT_SUCCESS(status))
935  {
936  ERROR("[ERROR] IntHookGpaSetHook failed on %llx: 0x%08x\n", pInv->Gva, status);
937  IntIcFreeInvdEntry(pInv);
938  return status;
939  }
940 
941  // Insert the invalidation line inside the list.
942  InsertTailList(&Cache->InsInvGva[invline], &pInv->Link);
943  }
944 
945  *Invd = pInv;
946 
947  return INT_STATUS_SUCCESS;
948 }
949 
950 
951 INTSTATUS
953  _In_ PINS_CACHE Cache,
954  _In_ PINSTRUX Instruction,
955  _In_ QWORD Gva,
956  _In_ QWORD Cr3,
957  _In_ BOOLEAN Global
958  )
978 {
979  INTSTATUS status;
980  DWORD line, target, i;
981 
982  if (Cache == NULL)
983  {
985  }
986 
987  if (Instruction == NULL)
988  {
990  }
991 
992  line = IntIcHashLine(Cache, Gva);
993 
994  target = 0xFFFFFFFF;
995 
996  for (i = 0; i < Cache->EntriesCount; i++)
997  {
998  if (!Cache->Lines[line].Entries[i].Valid)
999  {
1000  Cache->FillRate++;
1001 
1002  target = i;
1003 
1004  break;
1005  }
1006  }
1007 
1008  if (0xFFFFFFFF == target)
1009  {
1010  target = __rdtsc() % Cache->EntriesCount;
1011  }
1012 
1013  // Check if we need to decrement the refcount for the entry
1014  if (Cache->Lines[line].Entries[target].Valid)
1015  {
1016  status = IntIcInvdCacheEntry(Cache, &Cache->Lines[line].Entries[target]);
1017  if (!INT_SUCCESS(status))
1018  {
1019  ERROR("[ERROR] IntIcInvdCacheEntry failed: 0x%08x\n", status);
1020  }
1021  }
1022 
1023  // Mark the entry as invalid, until we completely initialize it - this includes an invalidation entry, if
1024  // needed.
1025  Cache->Lines[line].Entries[target].Valid = FALSE;
1026 
1027  Cache->Lines[line].Entries[target].Gva = Gva;
1028  Cache->Lines[line].Entries[target].Cr3 = Cr3;
1029  Cache->Lines[line].Entries[target].RefCount = 0;
1030  Cache->Lines[line].Entries[target].Pinned = FALSE;
1031  Cache->Lines[line].Entries[target].Global = Global;
1032 
1033  memcpy(&Cache->Lines[line].Entries[target].Instruction, Instruction, sizeof(INSTRUX));
1034 
1035  Cache->Lines[line].Entries[target].Invd1 = NULL;
1036  Cache->Lines[line].Entries[target].Invd2 = NULL;
1037 
1038  // Add the invalidation entry for this page.
1039  status = IntIcAddInvdForInstruction(Cache, Gva, Cr3, &Cache->Lines[line].Entries[target].Invd1);
1040  if (!INT_SUCCESS(status))
1041  {
1042  if ((INT_STATUS_PAGE_NOT_PRESENT != status) &&
1044  {
1045  ERROR("[ERROR] IntIcAddInvdForInstruction failed: 0x%08x\n", status);
1046  }
1047 
1048  return status;
1049  }
1050 
1051  // Handle instructions that cross the page boundary.
1052  if ((Gva & PAGE_MASK) != ((Gva + Instruction->Length - 1) & PAGE_MASK))
1053  {
1054  TRACE("[ICACHE] Instruction at %llx:%d spills the page...\n", Gva, Instruction->Length);
1055 
1056  status = IntIcAddInvdForInstruction(Cache, (Gva & PAGE_MASK) + 0x1000, Cr3,
1057  &Cache->Lines[line].Entries[target].Invd2);
1058  if (!INT_SUCCESS(status))
1059  {
1060  if ((INT_STATUS_PAGE_NOT_PRESENT != status) &&
1062  {
1063  ERROR("[ERROR] IntIcAddInvdForInstruction failed: 0x%08x\n", status);
1064  }
1065 
1066  IntIcInvdEntry(Cache, Cache->Lines[line].Entries[target].Invd1);
1067 
1068  return status;
1069  }
1070 
1071  // We need to know if this entry is the spill page, so we can properly invalidate the previous page in case
1072  // if swap/writes.
1073  Cache->Lines[line].Entries[target].Invd2->Spill = TRUE;
1074  }
1075 
1076  // Successfully added, the entry is now valid!
1077  Cache->Lines[line].Entries[target].Valid = TRUE;
1078 
1079  Cache->Dirty = TRUE;
1080 
1081  return INT_STATUS_SUCCESS;
1082 }
1083 
1084 
1085 INTSTATUS
1087  _Inout_ INS_CACHE **Cache,
1088  _In_ DWORD LinesCount,
1089  _In_ DWORD EntriesCount,
1090  _In_ DWORD InvCount
1091  )
1107 {
1108  INTSTATUS status = INT_STATUS_SUCCESS;
1109  INS_CACHE *pCache;
1110 
1111  if (NULL == Cache)
1112  {
1114  }
1115 
1116  // LinesCount and InvCount must be powers of two.
1117  if (0 != (LinesCount & (LinesCount - 1)))
1118  {
1120  }
1121 
1122  if (0 != (InvCount & (InvCount - 1)))
1123  {
1125  }
1126 
1127  pCache = HpAllocWithTag(sizeof(*pCache), IC_TAG_INSC);
1128  if (NULL == pCache)
1129  {
1131  }
1132 
1133  pCache->LinesCount = LinesCount;
1134  pCache->EntriesCount = EntriesCount;
1135  pCache->InvCount = InvCount;
1136 
1137  pCache->HitCount = 0;
1138  pCache->MissCount = 0;
1139  pCache->FillRate = 0;
1140  pCache->FlushCount = 0;
1141  pCache->ReplaceCount = 0;
1142  pCache->PageFlushCount = 0;
1143 
1144  pCache->Lines = HpAllocWithTag(sizeof(*pCache->Lines) * LinesCount, IC_TAG_INSC);
1145  if (NULL == pCache->Lines)
1146  {
1148  goto cleanup_and_exit;
1149  }
1150 
1151  for (DWORD i = 0; i < LinesCount; i++)
1152  {
1153  pCache->Lines[i].Entries = HpAllocWithTag(sizeof(*pCache->Lines[0].Entries) * EntriesCount, IC_TAG_INSC);
1154  if (NULL == pCache->Lines[i].Entries)
1155  {
1157  goto cleanup_and_exit;
1158  }
1159  }
1160 
1161  pCache->InsInvGva = HpAllocWithTag(sizeof(*pCache->InsInvGva) * InvCount, IC_TAG_INSC);
1162  if (NULL == pCache->InsInvGva)
1163  {
1165  goto cleanup_and_exit;
1166  }
1167 
1168  for (DWORD j = 0; j < InvCount; j++)
1169  {
1170  InitializeListHead(&pCache->InsInvGva[j]);
1171  }
1172 
1173 cleanup_and_exit:
1174  if (!INT_SUCCESS(status))
1175  {
1176  if (NULL != pCache)
1177  {
1178  if (NULL != pCache->InsInvGva)
1179  {
1181  }
1182 
1183  if (NULL != pCache->Lines)
1184  {
1185  for (DWORD i = 0; i < LinesCount; i++)
1186  {
1187  if (NULL != pCache->Lines[i].Entries)
1188  {
1190  }
1191  }
1192 
1194  }
1195 
1197  }
1198  }
1199  else
1200  {
1201  IntIcFlush(pCache);
1202  }
1203 
1204  *Cache = pCache;
1205 
1206  return status;
1207 }
1208 
1209 
1210 INTSTATUS
1212  _Inout_ PINS_CACHE *Cache
1213  )
1224 {
1225  INTSTATUS status;
1226  DWORD i;
1227  PINS_CACHE pCache;
1228 
1229  if (NULL == Cache)
1230  {
1232  }
1233 
1234  pCache = *Cache;
1235  if (NULL == pCache)
1236  {
1238  }
1239 
1240  status = IntIcRemoveAllInvdEntries(*Cache);
1241  if (!INT_SUCCESS(status))
1242  {
1243  ERROR("[ERROR] IntIcRemoveAllInvdEntries failed: 0x%08x\n", status);
1244  }
1245 
1246  for (i = 0; i < pCache->LinesCount; i++)
1247  {
1249  }
1250 
1252 
1254 
1256 
1257  return INT_STATUS_SUCCESS;
1258 }
BOOLEAN Spill
True if there is an instruction inside this entry that spills inside the next page.
Definition: icache.h:29
#define INT_STATUS_PAGE_NOT_PRESENT
Indicates that a virtual address is not present.
Definition: introstatus.h:438
QWORD PhysicalAddress
The physical address to which VirtualAddress translates to.
Definition: introcore.h:107
_Bool BOOLEAN
Definition: intro_types.h:58
#define _Out_
Definition: intro_sal.h:22
#define CONTAINING_RECORD(List, Type, Member)
Definition: introlists.h:36
BOOLEAN Pinned
True if the entry is pinned (it cannot be evicted).
Definition: icache.h:45
DWORD FillRate
How many entries or occupied by valid instructions.
Definition: icache.h:70
DWORD EntriesCount
Number of entries inside each line.
Definition: icache.h:65
#define _In_
Definition: intro_sal.h:21
INTSTATUS IntHookGpaRemoveHook(HOOK_GPA **Hook, DWORD Flags)
Remove a GPA hook.
Definition: hook_gpa.c:738
QWORD SystemCr3
The Cr3 used to map the kernel.
Definition: guests.h:211
#define INT_STATUS_SUCCESS
Definition: introstatus.h:54
INSTRUX Instruction
The decoded instruction.
Definition: icache.h:38
INTSTATUS IntIcAddInstruction(PINS_CACHE Cache, PINSTRUX Instruction, QWORD Gva, QWORD Cr3, BOOLEAN Global)
Adds an instruction to the cache.
Definition: icache.c:952
struct _LIST_ENTRY * Flink
Definition: introlists.h:20
#define IC_ANY_VAS
Definition: icache.h:86
static __inline DWORD IntIcHashLine(PINS_CACHE Cache, QWORD Gva)
Compute an instruction line index.
Definition: icache.c:32
static INTSTATUS IntIcWriteHandler(INS_CACHE_INV_ENTRY const *Context, void const *Hook, QWORD Address, INTRO_ACTION *Action)
Cached instruction page write handler.
Definition: icache.c:306
#define INT_SUCCESS(Status)
Definition: introstatus.h:42
static INTSTATUS IntIcInvdCacheEntry(PINS_CACHE Cache, PINS_CACHE_ENTRY Entry)
Invalidate an instruction cache entry.
Definition: icache.c:261
static BOOLEAN IsListEmpty(const LIST_ENTRY *ListHead)
Definition: introlists.h:78
#define PAGE_OFFSET
Definition: pgtable.h:32
QWORD Cr3
Virtual address space containing the instruction. Can be IC_ANY_VAS.
Definition: icache.h:40
INTSTATUS IntIcDestroy(PINS_CACHE *Cache)
Destroy an instruction cache.
Definition: icache.c:1211
#define ERROR(fmt,...)
Definition: glue.h:62
DWORD FlushCount
Number of times the cache has been flushed.
Definition: icache.h:71
#define HpAllocWithTag(Len, Tag)
Definition: glue.h:516
#define PHYS_PAGE_MASK
Definition: pgtable.h:38
int INTSTATUS
The status data type.
Definition: introstatus.h:24
QWORD Gva
The guest virtual page described by this entry.
Definition: icache.h:22
#define INT_STATUS_NOT_FOUND
Definition: introstatus.h:284
#define TRFLG_NONE
No special options.
Definition: introcore.h:82
LIST_ENTRY Link
List entry element.
Definition: icache.h:21
DWORD RefCount
Number of times this instruction has been hit.
Definition: icache.h:41
DWORD MissCount
Number of cache misses.
Definition: icache.h:69
BOOLEAN Valid
True if the entry is valid.
Definition: icache.h:44
INTSTATUS IntHookGpaSetHook(QWORD Gpa, DWORD Length, BYTE Type, PFUNC_EptViolationCallback Callback, void *Context, void *ParentHook, DWORD Flags, HOOK_GPA **Hook)
Places an EPT hook on the indicated memory range.
Definition: hook_gpa.c:193
#define LOG(fmt,...)
Definition: glue.h:61
INTSTATUS IntHookPtsSetHook(QWORD Cr3, QWORD VirtualAddress, PFUNC_SwapCallback Callback, void *Context, void *Parent, DWORD Flags, PHOOK_PTS *Hook)
Start monitoring translation modifications for the given VirtualAddress.
Definition: hook_pts.c:1535
static INTSTATUS IntIcInvdEntry(PINS_CACHE Cache, PINS_CACHE_INV_ENTRY Invd)
Decrements the reference count of the provided invalidation entry, and, if it reaches 0...
Definition: icache.c:219
INTSTATUS IntIcCreate(INS_CACHE **Cache, DWORD LinesCount, DWORD EntriesCount, DWORD InvCount)
Create anew instruction cache.
Definition: icache.c:1086
#define IC_TAG_IINV
Instruction cache invalidation entry.
Definition: memtags.h:30
void IntIcDumpIcache(void)
Dumps the entire contents of the implicit, per guest, instruction cache.
Definition: icache.c:55
#define _Inout_
Definition: intro_sal.h:20
static INTSTATUS IntIcRemoveAllInvdEntries(PINS_CACHE Cache)
Removes all the invalidation entries contained in this cache. This should be used only during uninit...
Definition: icache.c:179
DWORD PageFlushCount
Number of page flushes.
Definition: icache.h:73
static BOOLEAN RemoveEntryList(LIST_ENTRY *Entry)
Definition: introlists.h:87
unsigned long long QWORD
Definition: intro_types.h:53
static INTSTATUS IntIcFreeInvdEntry(PINS_CACHE_INV_ENTRY Invd)
Free an invalidation entry.
Definition: icache.c:136
INS_CACHE_ENTRY * Entries
Array containing the entries.
Definition: icache.h:55
#define TRUE
Definition: intro_types.h:30
#define INT_STATUS_INVALID_PARAMETER_4
Definition: introstatus.h:71
INTSTATUS IntIcFlushGvaPage(PINS_CACHE Cache, QWORD Gva, QWORD Cr3, BOOLEAN Spill)
Flush all entries cached from a given guest virtual page.
Definition: icache.c:664
void * SwapHook
Swap handle.
Definition: icache.h:26
static INTSTATUS IntIcLookupInstructionInternal(PINS_CACHE Cache, PINSTRUX Instrux, QWORD Gva, QWORD Cr3, DWORD Lru)
Lookup an instruction inside the cache.
Definition: icache.c:427
LIST_HEAD * InsInvGva
Array of invalidation entries.
Definition: icache.h:78
#define TRACE(fmt,...)
Definition: glue.h:58
#define HpFreeAndNullWithTag(Add, Tag)
Definition: glue.h:517
INTSTATUS IntIcFlush(PINS_CACHE Cache)
Flush the entire instruction cache.
Definition: icache.c:537
INTSTATUS IntHookPtsRemoveHook(HOOK_PTS **Hook, DWORD Flags)
Remove a PTS hook.
Definition: hook_pts.c:1944
static INTSTATUS IntIcSwapHandler(void *Context, QWORD VirtualAddress, QWORD OldEntry, QWORD NewEntry, QWORD OldPageSize, QWORD NewPageSize)
Cache instruction page swap handler.
Definition: icache.c:364
static void InsertTailList(LIST_ENTRY *ListHead, LIST_ENTRY *Entry)
Definition: introlists.h:135
INTSTATUS IntTranslateVirtualAddressEx(QWORD Gva, QWORD Cr3, DWORD Flags, VA_TRANSLATION *Translation)
Translates a guest virtual address to a guest physical address.
Definition: introcore.c:1863
INTSTATUS IntIcFlushGpaPage(PINS_CACHE Cache, QWORD Gpa)
Flush all entries cached from a given guest physical page.
Definition: icache.c:734
static void InitializeListHead(LIST_ENTRY *ListHead)
Definition: introlists.h:69
#define PAGE_SIZE
Definition: common.h:70
#define UNREFERENCED_PARAMETER(P)
Definition: introdefs.h:29
static INTSTATUS IntIcAddInvdForInstruction(PINS_CACHE Cache, QWORD Gva, QWORD Cr3, PINS_CACHE_INV_ENTRY *Invd)
Add invalidation entries for a newly cached instruction.
Definition: icache.c:852
void * InstructionCache
The currently used instructions cache.
Definition: guests.h:404
QWORD Gpa
The guest physical page described by this entry.
Definition: icache.h:23
uint32_t DWORD
Definition: intro_types.h:49
INTSTATUS IntIcLookupInstruction(PINS_CACHE Cache, PINSTRUX Instrux, QWORD Gva, QWORD Cr3)
Lookup an instruction inside the cache.
Definition: icache.c:495
DWORD LinesCount
Number of lines inside the cache. Must be a power of 2.
Definition: icache.h:64
enum _INTRO_ACTION INTRO_ACTION
Event actions.
static uint64_t __rdtsc(void)
Definition: intrinsics.h:306
#define IC_TAG_INSC
Instruction cache.
Definition: memtags.h:29
INTSTATUS IntIcFlushAddress(PINS_CACHE Cache, QWORD Gva, QWORD Cr3)
Flush entries cached from a given address.
Definition: icache.c:597
DWORD InvCount
Number of lines inside the invalidation array. Must be a power of 2.
Definition: icache.h:66
MM Mm
Guest memory information, such as paging mode, system Cr3 value, etc.
Definition: guests.h:374
GUEST_STATE gGuest
The current guest state.
Definition: guests.c:50
void * WriteHook
EPT write hook handle.
Definition: icache.h:25
INTSTATUS IntIcFlushVaSpace(PINS_CACHE Cache, QWORD Cr3)
Flush an entire virtual address space.
Definition: icache.c:797
#define INT_STATUS_NO_MAPPING_STRUCTURES
Indicates that not all mapping structures of a virtual address are present.
Definition: introstatus.h:434
static __inline DWORD IntIcHashInv(PINS_CACHE Cache, QWORD Gva)
Compute the invalidation entry index.
Definition: icache.c:10
#define NLOG(fmt,...)
Definition: glue.h:43
#define INT_STATUS_NOT_INITIALIZED_HINT
Definition: introstatus.h:320
Encapsulates information about a virtual to physical memory translation.
Definition: introcore.h:102
#define INT_STATUS_INVALID_PARAMETER_1
Definition: introstatus.h:62
#define INV_MASK
QWORD Gva
The instruction guest virtual address.
Definition: icache.h:39
INS_CACHE_LINE * Lines
Array of cache lines.
Definition: icache.h:77
Write-access hook.
Definition: glueiface.h:299
#define PAGE_MASK
Definition: pgtable.h:35
DWORD HitCount
Number of cache hits.
Definition: icache.h:68
#define INT_STATUS_INVALID_PARAMETER_2
Definition: introstatus.h:65
DWORD ReplaceCount
Number of times entries were evicted & replaced by other ones.
Definition: icache.h:72
BOOLEAN Global
True if the entry is global (shared in multiple processes).
Definition: icache.h:46
struct _INS_CACHE * PINS_CACHE
QWORD Cr3
Virtual address space the page belongs to.
Definition: icache.h:24
#define FALSE
Definition: intro_types.h:34
#define INT_STATUS_INSUFFICIENT_RESOURCES
Definition: introstatus.h:281