Instance lifetime - SESA/Umm GitHub Wiki

lifetime of running instance

So you want to run your executable on Um? Well, this is what ya gotta do.

  1. Create an SV from your Elf. This involves creating a region list which just maintains pointers back to the secions of the elf. It includes stack and heap sections which aren't in the ELF of course. It also creates a default exception frame, representing some inital state for the registers. This is mostly zeros except for some values that initialize the floating point regs and the CS segment register.

  2. Now we use that SV to create an instance, which wraps the SV and maintains some meta state necessary for execution. The SV you're using to create this instance may be coming directly from the ELF loader, in which case this is just a copy of a UmSV reference. Otherwise you're copying a snapshot, in which case this will involve a page table copy.

  3. Great, now we can inject solo5 boot arguments into the instance.

  4. Time to Load the instance. Two paths here depending if you have a page table (loading snap) or none (new from ELF). If you have a page table, slap it into PML4, and run. Otherwise leave it null & we'll start building out the table on the first page fault.

  5. Cool, we're loaded, time to run. This is nothing more than triggering a breakpoint exception (int3). We overloaded the breakpoint exception handler to act as a gateway into and out of the execution of instance code. On the way in, we need to store the current exception frame (that of runSV) which is our ticket back into our application. Next we overwrite the current ef with either the null ef of a newly loaded ELF, or the ef of a snapshot taken earlier. On returning we iret to the entry (or resume) point of the ELF (snapshot). This was set in the elf loader when the UmSV was created, or during the snapshot. Now we're in.

  6. There are 2 ways to exit the running instance. One is on a preconfigured debug exception, this is the mechanism used for taking a snapshot the other is via the "solo5_hypercall_halt" which is called after the rump unikernel finishes executing.

  7. On the solo5_hypercall_halt path, Halt() is called. If there are pending events on the core queue, Halt() spawns local a recursive call to itself (I believe this is to clear potential TCP state out of the queue). When there's nothing left on the queue, we clear proxy data and trigger the breakpoint exception mentioned above.

  8. This time through the gateway, we do the inverse of the entry, namely: restore the exception frame with the frame from runSV(). We then iret back into runSV which returns to the app.

createSVFromElf: Load sv out of elf. Sets up region list.

UmInstance(UmSV &sv): Page table copied here if necessary (from snapshot)

load(): Install root from umi if it exists (from snapshot), otherwise leave null to be populated during 1st page fault.

Unload(): Clears pml4 ent, flushes caches,

runSV(): Triggers bp exception by executing int3 instruction. Perculates down to process_gatewat

process_gateway(ef): Called from runSV and halt. The enter / exit point for the function execution. If the core is in the loaded position, we enter, if alerady running, we exit.

Enter: -Status is loaded, so store current ef for when we're done and need to return to umm code. -Overwrite ef with ef from sv. Initially mostly null, or use one from snap.

Exit: -Overwrite ef with stored version from enter

SetCheckpoint: Config debug exception on particular address. Returns future UmSV

process_checkpoint()