XV6 as most OSs is monolithic, preemptive and time-sharing multiplexing.

Isolation comes from the CPU which provides machine, supervisor and user modes.

- machine: just for the boot time
- supervisor: each time the kernel is active
  including when calling a syscall (with `ecall` in RISC-V)
- user: for all user code

Minix, L4 (seL4) and QNX operating systems have been mentioned as micro-kernel OSs.

Memory layout of a process:
  [text; data; user stack; heap (large) ; trapframe; trampoline]
0 ↑------------------------------------------------------------↑ 2^38-1

text: instructions
data: global variables
heap: explicitly allocated memory
trapframe: saved process registers when switching in and out of the kernel
trampoline (4 KiB): code to transition in and out of the kernel

trapframe & trampoline: explained in chapter 4

kernel `proc` structure contains:

- (kstack) kernel stack used when the process calls for syscalls
- (pagetable) pointers to physical memory pages actually used by the process,
              provided to the hardware for translation (virtual @ ←→ physical @)
- (state) UNUSED, USED, SLEEPING, RUNNABLE, RUNNING or ZOMBIE
- (trapframe) saved process registers when switching in and out of the kernel
- (pid) process identification number
- (ofile) list of opened files
- (name) process name
- (cwd) current directory
- (context) kernel registers used to enter the process
- (sz) size of process's memory
- (parent) pointer to the process's parent proc structure
- (xstate) exit status (given to its parent when it "waits" for it)
- (killed) non-zero when the process has been killed
- (chan) TODO: not currently explained
- (lock) TODO: not currently explained


RISC-V instructions

- ecall: raise hardware privilege level
  program counter change to a kernel-defined entry point which then switches to the
  process's kernel stack and executes kernel instructions for this syscall
  once done, the kernel calls sret
- sret: lower hardware privilege level

A process is the abstraction of memory and CPU for a running program,
giving it the illusion of being alone on the hardware.
A process is:

- an address space to give a running program the illusion of owning the entire memory
- a thread to give a running program the illusion of having a CPU for himself

starting xv6, the different phases

  context: booting up the machine
    => paging hardware disabled (virtual memory == physical memory)
    => booting at phy@ 0x80000000 because 0-0x80000000 contains IO devices
    => FYI: stack grows DOWN

  boot loader loads xv6 into memory then jumps to _entry (kernel/entry.S:7)

  phases for _entry:
    1. sets up a 4096-byte stack for each hardware thread (HART)
       (hart = "hardware thread" as opposed to software-managed thread context)
       these stacks start at the address "start0" defined in C code (kernel/start.c:11)
    2. loads stack0+4096 in sp (stack pointer)
       (which is the top of the first stack because stacks grow DOWN)
    3. jumps to C function "start" (kernel/start.c:14)

  phases for "start":
    => main idea: "start" performs machine-mode configuration then jumps to "main"
       ex: interruptions, exceptions and Physical Memory Protection configuration
    1. configures supervisor mode (related to the `mret` RISC-V instruction)
       mret enables to "return" from a mode to previous one
       mret in this case is first _configured_ to jump to supervisor mode
       a. mstatus (previous mode) is set to "supervisor"
       b. mepc (return address) is set to the address of "main" (kernel/main.c:10)
       c. satp (page-table register) is set to 0
          => disables virtual address translation in supervisor mode
       d. delegates all interruptions and exceptions to supervisor mode
    2. sets a timer interrupts on the clock chip
    3. changes to supervisor mode with `mret` while jumping to "main" (kernel/main.c:10)

  phases for "main":
    1. initializes devices, subsystems and a lot of stuff in general
    2. calls "userinit" (kernel/proc.c:233) to set up the first "user process"
       => it is just the creation of the process from a kernel point of view ≠ execution
       => the process is then in "RUNNABLE" state
       => the program for this process is in initcode.S (kernel/initcode.S:3)
          (but compiled for some reason into the kernel/proc:221 char array)
    3. calls the scheduler
       executes the only "RUNNABLE" process in the list, made by "userinit"
       => this "initcode" executes the /init application
          here is the code:
          1. a0 = address to the "/init" string
          2. a1 = argv for the future process
          3. EXEC syscall to run the init program with provided parameters (a0 & a1)

  phases for /init:
    1. creates a console device (if needed)
    2. opens file descriptors
    3. starts the shell on the newly created console

  system is up and running, yay!