+
+typedef enum {
+ INP_ALLG = 2, INP_ASPACE = 1, INP_SINGLE = 0, INP_ALLNG = 3
+} invpcid_type_t;
+typedef struct __attribute__((packed)) {
+ uint64_t ipcid_and_rsvd;
+ uint64_t iaddr;
+} invpcid_desc_t;
+
+static inline void
+invpcid(invpcid_type_t itype, pcid_t ipcid, uint64_t iaddr)
+{
+ invpcid_desc_t ipcdt;
+
+ ipcdt.ipcid_and_rsvd = ipcid;
+ ipcdt.iaddr = iaddr;
+
+ uint64_t iptype = itype; //promote to workaround assembler bug
+
+ __asm__ volatile ("invpcid %0, %1" :: "m" (ipcdt), "r" (iptype) : "memory");
+}
+
+
+void
+pmap_tlbi_range(uint64_t startv, uint64_t endv, bool global, uint16_t pcid)
+{
+ assert(ml_get_interrupts_enabled() == FALSE ||
+ get_preemption_level() != 0);
+
+ if (invpcid_enabled) {
+ if (global) {
+ invpcid(INP_ALLG, 0, 0ULL);
+ } else {
+ /* TODO: separate large page invalidation check */
+ if ((endv - startv) >= INP_MAX) {
+ invpcid(INP_ASPACE, pcid, 0ULL);
+ if (pcid) {
+ invpcid(INP_ASPACE, (pcid + PMAP_PCID_MAX_PCID), 0ULL);
+ }
+ } else {
+ uint64_t cv = startv;
+ for (; cv < endv; cv += PAGE_SIZE) {
+ invpcid(INP_SINGLE, pcid, cv);
+ if (pcid) {
+ invpcid(INP_SINGLE, (pcid + PMAP_PCID_MAX_PCID), cv);
+ }
+ }
+ }
+ }
+ } else {
+ if (pmap_pcid_ncpus) {
+ uintptr_t cr4 = get_cr4();
+ if (__improbable((cr4 & CR4_PGE) == 0)) {
+ set_cr4(cr4 | CR4_PGE);
+ } else {
+ set_cr4(cr4 & ~CR4_PGE);
+ set_cr4(cr4 | CR4_PGE);
+ }
+ } else {
+ set_cr3_raw(get_cr3_raw());
+ }
+ }
+ __c11_atomic_thread_fence(__ATOMIC_SEQ_CST);
+}