Early boot: Difference between revisions

From OLPC
Jump to navigation Jump to search
 
(24 intermediate revisions by 5 users not shown)
Line 1: Line 1:
;This page describes things that happen early in the boot process. Basically this is the startup of the ''first'' process that runs. This is the documentation for the peculiarities that allows for this process to run without things that most processes expect when they start running.
{{OLPC}}

{{developers}}
{{draft}}
{{draft}}
Draft of early boot upgrade/init procedures designed by Michael Stone and C. Scott Ananian.
Draft of early boot upgrade/init procedures designed by Michael Stone and C. Scott Ananian.
Line 7: Line 9:
=== Stage 1: Initramfs ===
=== Stage 1: Initramfs ===


''See the [http://dev.laptop.org/git?p=users/cscott/olpcrd-rootskel;a=tree;f=src-olpc;hb=HEAD source code] for more details. Instructions are available for [[Building initramfsen|building initramfsen]].''
''See the [http://dev.laptop.org/git?p=users/cscott/olpcrd-rootskel;a=tree;f=src-olpc;hb=HEAD source code] for more details. Instructions are available for [[building initramfs]].''


# Control is passed to the initramfs' [http://dev.laptop.org/git?p=users/cscott/olpcrd-rootskel;a=blob;f=src-olpc/init;hb=HEAD /init] program.
# Control is passed to the initramfs' [http://dev.laptop.org/git?p=users/cscott/olpcrd-rootskel;a=blob;f=src-olpc/init;hb=HEAD /init] program.
#* This program runs python2.5 as PID 1.
#* This program runs python2.5 as PID 1.
# /init executes the [[Theft deterrence protocol]], checking its 'am I stolen?' flag and looking for a valid[[Firmware Key and Signature Formats#Antitheft/Activation Lease activation lease]].
# /init executes the [[Theft deterrence protocol|theft deterrence protocol]], checking its 'am I stolen?' flag and looking for a valid [[Firmware Key and Signature Formats#Antitheft/Activation Lease|activation lease]].
# If successful, /init will fork and hand over control to user-land.
# /init will decide whether to fail the boot.
#* If the boot should stop, then a graphical error message will be displayed.

#* If the boot should continue, then /init will fork and will prepare to execute the userland init program.
...(sometime later, or immediately if already activated)


=== Stage 2: NAND ===
=== Stage 2: NAND ===


Since /init forked, we are now running as PID ''NNN'' rather than PID 1.
# callback (as pid NNN)

# mount /sysroot, unmount usb/sd
# Mount /sysroot and unmount our USB or SD devices so that our userland can make its own decisions about how to handle them.
# copy /security/lease to /sysroot/security/lease if first boot (activation)
# Fail the boot if we think we're stolen.
# parse chosen/bootpath, swing /versions/current
# If requested, swing /versions/current to point at the backup OS tree.
# make minimal userland context (mount --move /sysroot /)
#* If necessary, make the filesystem [http://dev.laptop.org/git?p=users/cscott/olpcrd-rootskel;a=blob;f=src-olpc/upfs.py;hb=HEAD upgradable].
# ideally protect PID 1, RTC <- vserver delta time
# Start the boot animation.
# Make a minimal userland context (e.g. <tt>mount --move /sysroot /</tt>).
# Take any measures that might help protect PID 1 and the real-time clock (RTC).


=== Stage 3: Userland ===
=== Stage 3: Userland ===

Perhaps, we're booting a custom userland. In that case, /sbin/olpc_init.py will contain something like:

|def run():
| os.exec('/sbin/init')

and will be imported and run by PID 1.

Perhaps we're booting from a backup OS. Assuming that we've only got one partition to deal with:


# make new config w/ swapped current and alt
# make new config w/ swapped current and alt
Line 48: Line 44:
# Make the ''/versions/running'' symlink point to ''pristine/<hash>''
# Make the ''/versions/running'' symlink point to ''pristine/<hash>''
# Set $current equal to the basename of readlink of ''/versions/running'' (which should be a hash)
# Set $current equal to the basename of readlink of ''/versions/running'' (which should be a hash)
# Make appropriate bind-mounts:
# mount /home /versions/run/$current/home (or /home from home partition)
# mount /security /versions/run/$current/security (or /security from boot partition)
#* mount /home /versions/run/$current/home (or /home from home partition)
# mount /versions /versions/run/$current/versions
#* mount /security /versions/run/$current/security (or /security from boot partition)
# chroot /versions/run/$current (mount --move ?)
#* mount /versions /versions/run/$current/versions
# ''chroot'' or ''mount --move'' into /versions/run/$current


Finally, as suggested above:
Finally, run the userland init program.
if exists '/sbin/olpc_init.py':
# Import and run the ''run()'' function from /sbin/olpc_init.py if it exists; otherwise,
sys.path = ['/sbin'] + sys.path
# Exec /sbin/init.
from olpc_init import run
run(<parameters?>)
else:
exec '/sbin/init --init'


== Notes on P_SF_RUN ==
== Notes on P_SF_RUN ==
P_SF_RUN:
off = allow mod = run from /versions/run/X
on = pristine = run from /versions/run/X
switch on->off: set the unlink flags on /versions/run
off->on: create immutably-tagged /versions/run/a,b from /versions/a,b


[[OLPC Bitfrost#P_SF_RUN|P_SF_RUN]] is a Bitfrost protection intended to control when running programs may modify the 'run' image of the current OS tree. ([[OLPC Bitfrost#P_SF_CORE|P_SF_CORE]] controls modifications to the pristine versions of OS trees.)

Suppose we're going to boot an OS tree named ''X''.

* When P_SF_RUN is switched ''off'', we should make ''/versions/run/X'' copy-on-write.
* When P_SF_RUN is switched ''on'', we should boot from an immutable copy of the pristine version of ''X''.

These rules mean that:

* If P_SF_RUN is ''off'' then we should ''allow modifications''.
* If P_SF_RUN is ''on'' then we should ''prohibit modifications''.

== List of directories in security partition ==
/security
== List of directories in boot partition ==
== List of directories in boot partition ==
/boot -> boot-versions/<hash>
/boot -> boot-versions/<hash>
Line 74: Line 76:
/boot-versions/<version>/{runos.zip,runrd.zip,etc}
/boot-versions/<version>/{runos.zip,runrd.zip,etc}
/boot-versions/<version>/alt -> ../<alternate version>
/boot-versions/<version>/alt -> ../<alternate version>

/security
== List of directories in root partition ==
== List of directories in root partition ==
/sys, /proc, /ofw vfs
/sys, /proc, /ofw vfs
Line 86: Line 88:
net connection drops & updater is restarted)
net connection drops & updater is restarted)
/versions/run/{hashes}
/versions/run/{hashes}
/security
/boot -> versions/boot/current/boot (backwards compatibility; don't use)
/boot -> versions/boot/current/boot (backwards compatibility; don't use)
/boot-alt -> versions/boot/alt/boot (backwards compatibility; don't use)
/boot-alt -> versions/boot/alt/boot (backwards compatibility; don't use)
Line 94: Line 95:


== Upgrade procedure ==
== Upgrade procedure ==

Upgrade procedure, creating new b from a (w.l.o.g)
Suppose that we want to create a new OS tree named ''$b'' where ''$b'' cryptographically identifies the [[contents manifest specification|contents]] of the desired OS tree.)
Rainbow: (ATC gives <version> <hash> <priority>)

-1: Check that /versions/pristine/<hash> doesn't already exist.
# Check that /versions/pristine/<hash> doesn't already exist.
If unpartitioned:
# Let ''$a'' = ''`basename(readlink /versions/running)`''.
0. Create new /versions/configs/$c <- where $c = mkdtemp

1. Create /versions/configs/$c/current -> ../../pristine/`basename(readlink /versions/running)`
=== Create a "safety" boot configuration ===
2. Swap /versions/boot to point to configs/$c, save old contents in $old
If we are unpartitioned, then we may:
If partitioned:

0-2. Make /boot/alt point to ../`basename(readlink /versions/running)`
3a. Delete the tree(s) pointed to from /versions/configs/$old which are not pointed to by
# Create new /versions/configs/$c <- where $c = mkdtemp
/versions/running (revisit when multiple trees)
# Create /versions/configs/$c/current -> ../../pristine/$a
# Swing /versions/boot to point to configs/$c, save old contents in $old
3b. Delete corresponding members of /boot-versions if using a boot partition
4. Delete /versions/configs/$old.
: ''(If partitioned: Make /boot/alt point to ../$a.)''
4b. Delete corresponding member of /boot-versions if using a boot partition.

5. Invoke 'olpc-updater <version>'
Afterward, we should:
in new container:

[MICHAEL WILL REWRITE STARTING FROM HERE]
=== Make Space ===
NOTE THAT /upgrade must live in same bind-mount as /current if we're to be able to clone it.

MORE LIKELY THAT RAINBOW WILL CREATE /upgrade FOR US AS CLONE OF /current
/current (ro-bind mount from /versions/a)
# Delete the tree(s) pointed to from ''/versions/configs/$old'' which are:
#* not pointed to by ''/versions/running''
/upgrade (initially empty)
#* not pointed to by a symlink in ''/versions/sticky''
OLPC updater:

6. clone /current to /upgrade
: ''(If partitioned and if using a boot partition: delete corresponding members of /boot-versions.)''
7. upgrade /upgrade by hook or crook

[END MICHAEL REWRITES]
# Delete /versions/configs/$old.
8. exit
''(If partitioned and if using a boot partition: delete corresponding member of /boot-versions.)''
Rainbow:

9. Verify /versions/updates/<hash> matches <hash>
=== Acquire the Bits ===
10. Move /versions/updates/<hash> to /versions/pristine/<hash>

10b. Create /versions/run/<hash> from /versions/pristine/<hash> according to P_SF_RUN setting
# Shallow-copy ''/versions/pristine/$a'' into a tmpdir ''$d'' on the same file system. (To ''shallow-copy'' a tree is to copy its directory structure, then to hardlink all its inodes into the new empty tree.)
If unpartitioned:
# Modify the contents of ''$d'' by any means that breaks hardlinks before writing through them. When finished,
11. Make a new config /versions/configs/$d (d = mkdtemp)
# Cryptographically [[Olpc-contents|verify]] that ''$b'' identifies the contents of the tmpdir ''$d''.
12. Create 'current' symlink to /versions/pristine/<hash>

13. Create 'alt' symlink to *realpath of* /versions/running
=== Clean Up ===
14. Swing /versions/boot to /versions/configs/$d

(atomic! iff we do file move of new symlink)
If verification fails, destroy ''$d'' and return failure. Otherwise:
15. Delete /versions/configs/$c

If partitioned:
11. Copy /versions/pristine/<hash>/boot to boot:/boot-versions/<hash>
# Move ''$d'' to ''/versions/pristine/$b''.
# Create ''/versions/run/$b'' from ''/versions/pristine/$b'' according to the current [[OLPC Bitfrost:P_SF_RUN|P_SF_RUN]] setting.
12. Make boot:/boot-versions/<hash>/alt point to what boot:/boot currently points to

13. Atomically swing boot:/boot to point to /boot-versions/<hash>
Then, if unpartitioned:
16. If <priority> reboot. (Ask Eben & sugar folks)

# Make a new config in ''/versions/configs/$e'' with ''mkdtemp''.
# Point its 'current' symlink to ''/versions/pristine/$b''.
# Point its 'alt' symlink to ''(realpath /versions/running)''.
# ATOMICALLY swing ''/versions/boot'' to ''/versions/configs/$e''.
#* (To atomically swing a symlink $s, make a new symlink $t on the same filesystem pointing to the desired location and then rename ''$t'' on top of ''$s''.)
# Delete /versions/configs/$c
If partitioned:

# Copy ''/versions/pristine/$b/boot'' to ''boot:/boot-versions/$b''
# Make ''boot:/boot-versions/$b/alt'' point to ''(realpath boot:/boot)''.
# Atomically swing ''boot:/boot'' to point to ''boot:/boot-versions/%b''

Finally, execute any post-update instructions.


==Open Questions==
==Open Questions==
Line 155: Line 171:


==Related pages==
==Related pages==
* [[Boot process]] describes what the [[firmware]] does ''before'' the Linux boot sequence.
* [[Installing Debian as an upgrade]]
* [[Installing Debian as an upgrade]]
* [[Manual in-place upgrade]]
* [[Manual in-place upgrade]]


[[Category:software]]
[[Category:software]] [[Category:XO startup]]

Latest revision as of 22:31, 27 August 2009

This page describes things that happen early in the boot process. Basically this is the startup of the first process that runs. This is the documentation for the peculiarities that allows for this process to run without things that most processes expect when they start running.



Pencil.png NOTE: The contents of this page are not set in stone, and are subject to change!

This page is a draft in active flux ...
Please leave suggestions on the talk page.

Pencil.png

Draft of early boot upgrade/init procedures designed by Michael Stone and C. Scott Ananian.

Early userland startup steps

Stage 1: Initramfs

See the source code for more details. Instructions are available for building initramfs.

  1. Control is passed to the initramfs' /init program.
    • This program runs python2.5 as PID 1.
  2. /init executes the theft deterrence protocol, checking its 'am I stolen?' flag and looking for a valid activation lease.
  3. /init will decide whether to fail the boot.
    • If the boot should stop, then a graphical error message will be displayed.
    • If the boot should continue, then /init will fork and will prepare to execute the userland init program.

Stage 2: NAND

Since /init forked, we are now running as PID NNN rather than PID 1.

  1. Mount /sysroot and unmount our USB or SD devices so that our userland can make its own decisions about how to handle them.
  2. Fail the boot if we think we're stolen.
  3. If requested, swing /versions/current to point at the backup OS tree.
  4. Start the boot animation.
  5. Make a minimal userland context (e.g. mount --move /sysroot /).
  6. Take any measures that might help protect PID 1 and the real-time clock (RTC).

Stage 3: Userland

  1. make new config w/ swapped current and alt
    • (ie. create a /versions/configs/XXX w/ new current, alt)
  2. then swing /versions/boot symlink

If multiple partitions are present:

  1. Make boot:/boot/alt/alt point to ../`basename(readlink boot:/boot)`
  2. Make boot:/boot point to boot-versions/`basename(readlinke boot:/boot/alt)`

In either case:

  1. Make the /versions/running symlink point to pristine/<hash>
  2. Set $current equal to the basename of readlink of /versions/running (which should be a hash)
  3. Make appropriate bind-mounts:
    • mount /home /versions/run/$current/home (or /home from home partition)
    • mount /security /versions/run/$current/security (or /security from boot partition)
    • mount /versions /versions/run/$current/versions
  4. chroot or mount --move into /versions/run/$current

Finally, run the userland init program.

  1. Import and run the run() function from /sbin/olpc_init.py if it exists; otherwise,
  2. Exec /sbin/init.

Notes on P_SF_RUN

P_SF_RUN is a Bitfrost protection intended to control when running programs may modify the 'run' image of the current OS tree. (P_SF_CORE controls modifications to the pristine versions of OS trees.)

Suppose we're going to boot an OS tree named X.

  • When P_SF_RUN is switched off, we should make /versions/run/X copy-on-write.
  • When P_SF_RUN is switched on, we should boot from an immutable copy of the pristine version of X.

These rules mean that:

  • If P_SF_RUN is off then we should allow modifications.
  • If P_SF_RUN is on then we should prohibit modifications.

List of directories in security partition

 /security

List of directories in boot partition

 /boot -> boot-versions/<hash>
 /boot-alt -> boot/alt
 /boot-versions/<version>/{runos.zip,runrd.zip,etc}
 /boot-versions/<version>/alt -> ../<alternate version>

List of directories in root partition

 /sys, /proc, /ofw   vfs
 /versions/pristine/{hashes}
 /versions/contents/{hashes}  (contents files for the corresponding pristine tree)
 /versions/configs/`mkdtemp`/current -> ../../pristine/<hash> (backwards compatibility; don't use)
 /versions/configs/`mkdtemp`/alt     -> ../../pristine/<hash> (backwards compatibility; don't use)
 /versions/boot -> configs/<something> (backwards compatibility; don't use)
 /versions/running -> pristine/<hash>  (version we booted from; hash matches /boot symlink from boot partition)
 /versions/updates/<hash>   (temporary space for updates, preserved in case update
                             net connection drops & updater is restarted)
 /versions/run/{hashes}
 /boot -> versions/boot/current/boot (backwards compatibility; don't use)
 /boot-alt -> versions/boot/alt/boot (backwards compatibility; don't use)

List of directories in home partition

 /home

Upgrade procedure

Suppose that we want to create a new OS tree named $b where $b cryptographically identifies the contents of the desired OS tree.)

  1. Check that /versions/pristine/<hash> doesn't already exist.
  2. Let $a = `basename(readlink /versions/running)`.

Create a "safety" boot configuration

If we are unpartitioned, then we may:

  1. Create new /versions/configs/$c <- where $c = mkdtemp
  2. Create /versions/configs/$c/current -> ../../pristine/$a
  3. Swing /versions/boot to point to configs/$c, save old contents in $old
(If partitioned: Make /boot/alt point to ../$a.)

Afterward, we should:

Make Space

  1. Delete the tree(s) pointed to from /versions/configs/$old which are:
    • not pointed to by /versions/running
    • not pointed to by a symlink in /versions/sticky
(If partitioned and if using a boot partition: delete corresponding members of /boot-versions.)
  1. Delete /versions/configs/$old.

(If partitioned and if using a boot partition: delete corresponding member of /boot-versions.)

Acquire the Bits

  1. Shallow-copy /versions/pristine/$a into a tmpdir $d on the same file system. (To shallow-copy a tree is to copy its directory structure, then to hardlink all its inodes into the new empty tree.)
  2. Modify the contents of $d by any means that breaks hardlinks before writing through them. When finished,
  3. Cryptographically verify that $b identifies the contents of the tmpdir $d.

Clean Up

If verification fails, destroy $d and return failure. Otherwise:

  1. Move $d to /versions/pristine/$b.
  2. Create /versions/run/$b from /versions/pristine/$b according to the current P_SF_RUN setting.

Then, if unpartitioned:

  1. Make a new config in /versions/configs/$e with mkdtemp.
  2. Point its 'current' symlink to /versions/pristine/$b.
  3. Point its 'alt' symlink to (realpath /versions/running).
  4. ATOMICALLY swing /versions/boot to /versions/configs/$e.
    • (To atomically swing a symlink $s, make a new symlink $t on the same filesystem pointing to the desired location and then rename $t on top of $s.)
  5. Delete /versions/configs/$c

If partitioned:

  1. Copy /versions/pristine/$b/boot to boot:/boot-versions/$b
  2. Make boot:/boot-versions/$b/alt point to (realpath boot:/boot).
  3. Atomically swing boot:/boot to point to boot:/boot-versions/%b

Finally, execute any post-update instructions.

Open Questions

  1. Are thawed trees persistent?
    1. when I use a frozen tree?
    2. when I upgrade
  2. Is "thawness" global? Or per-OS-version?
  3. Can thawed trees be frozen for temporary read-only use?
  4. Space limits for upgrader?
  5. UI for:
    1. P_SF_RUN
    2. which image you boot (esp if more than two)
    3. Rest of security UI
  6. Configuration versioning / globalness
    1. do security settings persist across updates
    2. do we inherit a security configuration from the 'old' version when upgrading?
  7. Loadable kernel modules
    1. Bind-mount /lib/modules read-only? (Doesn't fix the problem, really)

Related pages