Prologue
I am an operating system hobbyist, and my kernel runs on 80486+, and already supports virtual memory.
Starting from 80386, the x86 processor family by Intel and various clones thereof has supported virtual memory with paging. It is well known that when the PG
bit in CR0
is set, the processor uses virtual address translation. Then, the CR3
register points to the top-level page directory, that is the root for 2-4 levels of page table structures that map the virtual addresses to physical addresses.
The processor does not consult these tables for each virtual address generated, instead caching them in a structure called Translation Lookaside Buffer, or TLB. However, when changes to the page tables are made, the TLB needs to be flushed. On 80386 processors, this flush would be done by
reloading (MOV
) CR3
with the top level page directory address, or a task switch. This supposedly unconditionally flushes all the TLB entries. As I understand, it would be perfectly valid for a virtual memory system to always reload CR3 after any change.
This is wasteful, since the TLB would now throw out completely good entries, thus in 80486 processors the INVLPG
instruction was introduced. INVLPG
will invalidate the TLB entry matching the source operand address.
Yet starting with Pentium Pro, we also have global pages that are not flushed with the moves to CR3
or task switch; and AMD x86-64 ISA says that some upper level page table structures might be cached and not invalidated by INVLPG
. To get a coherent picture of what is needed and what is not needed on each ISA one would really need to download a 1000-page datasheet for a multitudes of ISAs released since 80s to read a couple pages therein, and even then the documents seem to be particularly vague as to the TLB invalidation and what happens if the TLB is not properly invalidated.
Question
For the simplicity, one can assume that we are talking about a uniprocessor system. Also, it can be assumed that no task-switch is required after changing the page structures. (thus INVLPG
is always supposedly at least as good choice as reloading the CR3
register).
The base assumption is that one would need to reload CR3
after each change to page tables and page directories, and such a system would be correct. However, if one wants to avoid flushing the TLB needlessly, one needs answers to the 2 questions:
Provided that
INVLPG
is supported on the ISA, after what kind of changes can one safely use it instead of reloading theCR3
? E.g. "If one unmaps one page frame (set the corresponding table entry to not present), one can always useINVLPG
"?What kind of changes one can do to the tables and directories without touching either
CR3
or executingINVLPG
? E.g. "If a page is not mapped at all (not present), one can write a PTE withPresent=1
for it without flushing the TLB at all"?
Even after reading a quite a load of ISA documents and everything related to INVLPG
here on Stack Overflow I am not personally sure of either examples I presented there. Indeed, one notable post stated it right away: "I don't know exactly when you should use it and when you shouldn't." Thus any certain, correct examples, preferably documented, and for either IA32 or x86-64, that you can give, are appreciated.