With paging, all memory is allocated or mapped in units of one
page (4K for x86)

Address spaces.

When and when not to change address spaces.

When TLB invalidates are required. Full versus partial TLB
invalidation. Tagged TLBs (not a feature of x86 CPU).

Getting at memory in alternate address spaces (e.g. to service
a disk request)

Demand-loading: improves performance and conserves memory by
allocating, mapping, and optionally loading or zeroing each
page memory only when it's first accessed
xxx - demand-load page tables for task virtual memory?

Shared memory between tasks, e.g. for DLLs or IPC. Shared
copy-on-write memory (for fork() operation). Other shared memory
(e.g. framebuffer in task data segment)

Memory-mapped files.

Swapping. Choosing pages to swap out. Clock algorithm. Working sets.

Page allocation, including page coloring.

Disk cache read-ahead; sector or track sizes vs. page size



Real-mode IVT and BIOS data segment occupy physical page 0

Kernel/task memory = .text .rodata .data .bss   heap stack

xxx - where are the page tables?
xxx - sbrk() vs. mmap()?

Bitmap or stack to track physical memory allocation

Page table and page directory entires go into their own
cache (the TLB), so they can be placed in "slow" memory.

- virtual framebuffers in kernel heap

xxx - the second-level page tables for user memory of the
current task may also be not directly accessible -- use that
trick where where one entry in the page directory points to the
page directory itself
- memory mapping to access page directory entries and page
  tables using only virtual addresses





MUST BE IDENTITY-MAPPED
- Top-level page tables (page directories; one per task).
  Reason: the CR3 register (PDBR) must contain the physical
  address of the page directory. (xxx - but you can still
  use virtual addresses to get at the PDEs, can't you?)

- Kernel-privilege stacks (one per task or kernel thread).
  Reason: xxx - no reason?

- Physical VGA framebuffer
  Reason: xxx - no reason?


xxx - the second-level page tables for user memory of the
current task may also be not directly accessible -- use that
trick where where one entry in the page directory points to the
page directory itself

================================================================
WALK-THROUGH OF/PSEUDOCODE FOR PAGE FAULT HANDLER
================================================================
1.   Page fault caused by not-present page
2.   Handler checks fault address, figures out what to do
     (e.g. demand-load from executable file)
3.   Convert fault address to offset into executable file
4.   Filesystem code converts file offset into number of 4K
     block(s) on disk
a5.  "Hey, this block is already loaded" (e.g. it's a DLL). Just
     make a new memory mapping for the block for the current
     task and increase the usage count of the block. If the page
     is writable, make it copy-on-write.
        (time passes)
a6.  Page fault caused by write to copy-on-write page.
        ...

b5.  Block is not loaded. Allocate a page of memory for the load
     (< 16 meg if block device uses ISA DMA). Map it temporarily
     into the kernel heap, and lock it so it can't be swapped or
     stolen.
b6.  Queue disk request, sleep until done
        zzzzzz
b7.  Disk read done. For PIO device, REP INSW or REP INSD to the
     newly-allocated memory block in the kernel heap.
b8.  Kernel makes a note for the sleeping task:
        Before this task runs again, map this newly-loaded block
        into its page tables.
b9.  Wake up task
b10. Woken task is scheduled to run. Map the loaded block into
     the address space of the task, and un-lock the block.
     Maybe unmap the block from the kernel heap?
        ...

================================================================
VM initialization and bootstrapping
================================================================
- Initial kernel page directory, page tables, and stack are
  included in the kernel BSS.

- can access local variables (on stack) and, to some degree,
  code (.text segment, position-independent EIP-relative code)
  even if virtual addresses are wrong

- avoid use of global and static local variables (in .data and
  .bss segments) until paging is set up
    - asm code can use relative addressing to get at these
      variables.
    - could use segment-based address translation until paging
      is set up (x86 only)

- you can probably assume the kernel is loaded into contiguous
  physical memory...supporting discontiguous memory is too
  difficult for a bootloader or VM initialization code

- large kernel (>3 meg) or kernel booted from DOS may straddle
  4 meg line: needs two page tables to map its memory

- leave page 0 mapped at first, to get at the BIOS data segment.
  Unmap it when you're done using it, or copy out the data you
  need and then unmap it. This is to catch NULL pointer bugs.

================================================================
Conserving virtual memory
================================================================
Eventually, your OS will run on machines with a lot of memory
(e.g. 3 Gbytes), and the OS will run large apps (e.g. database
server) that can use all of that memory. You'll want to give
the app as much memory as possible by

1. no page of memory should be accessible with more than one
   type of address: physical, task virtual, or kernel virtual:

                                present
                                in all  typical
        memory      identity-   address virtual
        range       mapped?     spaces? address
        ------      ---------   ------- -------
        physical    yes         yes     bottom of adr space
        task        no          no      middle of adr space
        kernel      no          yes     top of address space


2. minimize virtual memory used by the kernel:
        kernel starting address         kernel memory used
        -----------------------         ------------------
        0xC0000000                      1 Gbyte
        0xE0000000                      0.5 Gbytes
        0xFFC00000                      4 Mbytes

3. allocate pages of memory that are not directly accessible
   e.g. page tables
