OK, it seems like a smart idea to have the IO permission bitmap per process instead of per thread. I could easily make the V86 thread a new process instead of just an ordinary kernel-thread.
I have a somewhat different layout of the per-CPU data today:
page 1 + most of page 2: per-core data (requires this much space because of timer data structures)
last 0x48 bytes of page 2: per-core GDT entries (especially the selector that is mapped to the per-core data)
page 3-17: shared part of GDT
I could change it to something like this:
page 1: core private stack + double-fault TSS + core TSS
page 2: IO permission bitmap for core TSS, part 1
page 3: IO permission bitmap for core TSS, part 2
page 4: a number of 1s + first part of per-core data
page 5: second part of per-core data + 0x70 bytes of private GDT (which includes the core private selector, a new core stack selector, a new core TSS selector, a new writable core TSS alias selector, a new double-fault TSS and alias selector)
page 6-20: shared part of GDT
When changing process, the scheduler would need to change mappings of page 2 and 3. On every thread change, it would need to update the ESS0 field in the core TSS.
An alternative approach would be to pre-allocate two pages with the default IO permission bitmap, and keep those two physical addresses in the thread control block (IOW,
it would still be per-thread). When a new thread is created, it would simply inherit the two pages. When the V86 bios thread wants its own (allow all) bitmap, it simply
allocates a 8k memory block, fills it with 0s, and places the two pages in the thread control-block. The scheduler would then remap page 2 and 3 if any of the physical
addresses changes instead, and if they do, force a CR3 flush.
This would only consume two GDT entries for TSS regardless of number of CPUs in the system.
Adding the double-fault TSS to the private core data makes it possible to have one double-fault TSS per core, which eliminates the problem of several cores hitting double-fault at the same time triggering tripple-faults to to busy double fault TSS. The IDT entry for double fault would be a task gate pointing to a fixed TSS selector, which has different mappings in different cores.