Won't such a basic protection mechanism missing in Dosbox break software(writing to r/o segments)?
Also, UniPCemu 'cheats' just a little bit, when prefetching the PIQ to become as full as possible, by checking the first byte to fetch, then the last one(to allow page faults to happen), working back in virtual memory(taking limit or 4GB as the max offset) until no page fault is encountered(so it honours the reading until page barrier(4KB chunks) if the next one isn't available). It does so by first checking if the next page(if any) is valid to fetch(for remainder of PIQ bytes to fetch). If it isn't paged(or page faults when supposed to), it rounds fetching down to the current page, allowing the EU(which still handles those) to page fault when it tries to access past said barrier, filling the remainder of the PIQ(usually the full PIQ or something close) it was unable due to being unpaged.
So, an instruction on the page barrier(4KB chunk of linear address space) will prefetch up to FFFh(or until the PIQ is full). Then, when the EU tries to check and fetch x000h(and onwards, depending on the PIQ filled state), the EU will handle the page loading into the TLB(and page fault, if any) and recall the prefetch routine explained above, which will fetch bytes x000+ into the PIQ(since it's paged now).
The memory access scheme(mmu handlers) are optimized somewhat for linear accesses within 64kb memory areas(see source code of mmuhandler.c) within the memory hole and mapping function(using a small cache data structure to store the most recently read and write memory translation(containing frame(64k) address to substract(to map memory to actual RAM array without memory holes in it, saving memory for each hole(size of the memory holes themselves adding memory after it instead) for the RAM area(if not a memory hole), memory hole status(which memory hole(for special Compaq BIOS ROM shadow area at (FF)E0000-(FF)FFFFF).
So, 0-640K is direct mapped, 640K-1M is at 1M+, 1M-almost 16M is after that, the remainder of RAM being at 16M-3G and after 4GB(for any more RAM). So 3 memory holes moves after(1M memory hole, 16M hole and 4G hole), moving their memory further to the back. 1M+ substracts (1M-640K), 16M+ substracts((1M-640K)+(16M-15M)) and 4G+ substracts (1M+640K)+(16M-15M)+(4G-3G) respectively for mapping the block to the actual emulated RAM allocated array.