Yes. V7 and other early unix had a 'stand alone' framework. So that's one
piece of the puzzle. It could be used for anything (I've seen private
diagnostic stand alone programs at Solbourne that would do more extensive
tests than the boot roms did for RMA and manufacturing line acceptance).
But as they come down to us from AT&T, they are just the bits needed to
install a system from scratch (or in some cases fix a system by booting the
distribution tapes).
One pitfall I would like to avoid in my own thinking
is conflating
“installing” and “booting”, even though the two seem related (one loads
bits into permanent storage, the other into volatile storage; both are
built around incrementally adding capability). When variable hardware comes
into play, the two mix even more. Also related is the topic of recovery.
There's "booting" and then there's "bootstrapping". booting
happens all the
time, but installation happens rarely. The problem is that the install
process for V7 and earlier systems was to load a series of programs into
memory from tape that did each step of the process to stay within the 64k
limits imposed by the platform. That process was done by a series of
standalone programs that could, in some cases, act as system repair for
certain cases of 'afu' system.
The starting point seems to be a setup where a small
set of standalone
programs is used to load or repair the bits, as was done for 16-bit unix.
Yea, that's where stand came from originally: An environment that could run
a limited program w/o booting the kernel...
Yes. It was quite common for the next step to be when demand paging became
a thing. Once demand paging became a thing and you didn't need a dedicated
swap partition, you could 'borrow' the installed system's swap partition
(or what would become the installed system's swap partition) to have a
richer set of tools. This would use otherwise wasted space for the install,
then you'd overwrite it with whatever workload you put on your system.
Another conceptual step might be where early installer
phases run a
different, smaller kernel (or even OS) than the one being installed. There
seems to be much potential for a “wheel of reincarnation” here where as an
installer grows large, a pre-installer is created to load the installer and
then the pre-installer grows large, etc. For booting, this wheel seems to
have turned about 5 times in current Linux. Installing that from scratch on
a fully blank SBC (without prepping a removable disk on another computer)
also appears to have 4 or 5 revolutions. That is one revolution every 5
years for the 25 years from the late seventies to the early 2000’s.
Indeed, the whole linuxboot saga where you boot a minimal linux kernel that
has a shell script (or similar) that decides where to load the complete
kernel from is also interesting (doubly so to me because it means I can
sneak in at that point and load FreeBSD onto the system).
Then there is the question of where in the
"installer stack" to stop. My
current interest excludes (precursors to) package managers, containers, etc.
Yes. For me, the 'preboot environment' is a better mental framework to look
at it with. Or 'pre-kernel' or 'non-kernel' environments. Though that
paradigm runs out of steam in the 90s when people started booting kernels
with bundled ram disks (in various flavors) to do the installation to allow
boot medium to be used to load additional data (eg the system). The
read/write nature of RAM made this vector useful, even when CDROMs appeared
on the scene. It was easier to run off a ram disk than off the CDROM
because you don't have to have symlinks to a (small) RAM disks for the bits
of the system that needed to be read/write during the install process...
The 'standalone' stuff, as typified by the src/stand directory, was all
pre-kernel or non-kernel use-cases. Sometimes it was used to build a bit of
code that had to be small, but was also used to build code to load the
kernel (even dating back to the original boot loader for V7, though I'm
relying on my memory which may be confusing that with the 2BSD
bootstrap)... The stuff that was 'standalone-but-on-a-unix-kernel' came
later.
It might also be amusing to note: I once contemplated porting V7 unix to
replace FreeBSD's stand environment to replace the various ad-hock hacks
that had grown up. But I found that those hacks were smaller in the end,
and needed less code space to support more things (excluding crypto and
compression) than the V7 did once you started looking at modifying it to
support multiple filesystem types, adding a network stack etc. While V7 is
nice and clean, it's also immature in terms of features needed for a modern
loader...
Warner
In short: much to ponder, thanks to all for sharing
recollections.
On 4 Sep 2023, at 19:07, Warner Losh
<imp(a)bsdimp.com> wrote:
On Mon, Sep 4, 2023 at 3:58 AM Paul Ruizendaal via TUHS <tuhs(a)tuhs.org>
wrote:
Recently, I was looking into the “Das U-Boot” boot loader package.
Summarised with
great simplification, u-boot bundles device drivers, file
systems, commands and a Bourne-like shell into a standalone package.
Normally it auto-runs a script that brings up a system, but when used in
interactive mode it allows a great deal of poking around.
It made me think of the “standalone” set of programs for installing
early Unix. On
16-bit understandably each basic command has to be a
separate standalone program, but after the shift to 32-bit bundling more
functionality in a single binary would have become possible.
How did the Unix “standalone” package evolve in the 80’s, both in the
research and
BSD lineages? Is there any retrospective paper about that? Or
is it a case of “Use the source, Luke”?
The stand package continued in research and BSD to be those programs
needed to
install and/or recover
badly damaged systems. You could create a new
file system, copy a file
from the tape to a partition, etc.
You couldn't do general scripting with this,
by and large.
Originally, they were tape programs. This made sense because of its
original
focus. In time, some systems
could load the stand alone programs instead of
the kernel, but they
continued the original focus.
This is, imho, due in large part due to the miniroot. The miniroot
evolved into
both a full-enough system
to do the installation scripts in shell instead
of C (Venix, at least,
had their install program written in C).
You'd copy the minroot to swap and then
install the system. But a number
of additional programs were
placed into the miniroot so you could do some
limited filesystem repair,
file editing, etc.
In addition, many vendor's ROMs grew in complexity. Solbourne's ROMs,
for example, could do basic
repair of UFS (clri level, not fsck level), and
copy files from one
place to another. I often recovered a
Solbourne system I screwed up by attaching an
external SCSI drive that
had a known good kernel,
init, etc.
The 'stand' environment was a whole set of tools that could be used to
build stand-alone programs that
shared much code of their full unix brethren,
despite not having a full
kernel under them. Kernel services
were provided by different libraries that did
filesystem things, block
driver things, network things, etc
in a similar way to Unix, but with a much reduced
footprint.
initramfs, as has been mentioned elsewhere, is pretty much a Linux
invention. It
was designed to
'punt' on the choose where to load
things from and have a very minimal
interface between the boot
loader and the system. In time, it grew to
support more interfaces, more
ways of loading, and better
ways to mount something that you could then
'pivot' onto. Few other unix
systems went this route, though
many adopted some variation on the pivot_root
functionality. Linux has
moved beyond the pivot root after
having booted the correct kernel into being able
to take over the
machine early in, say, UEFI startup with
a minimal kernel and initramfs that just knows
how to load the next
kernel. They skipped the complex boot
loader stage, and went straight to the 'run
linux earlier' stage which
is how things like LinuxBoot, coreboot
and others have put the boot logic into bash
scripts. The ability to
'kexec' a kernel and replace the current
running kernel originated in the
'non-stop' world that wanted to reduce
downtime. Now, it's used to
reduce
firmware complexity by eliminating large swaths
of UEFI from the boot
process, but also generalizes in
the embedded space.
FreeBSD, from around FreeBSD 2 (1995 or so), had /rescue which largely
took over
form the stand alone environment
for the repair duties of things. FreeBSD also
adopted a more complex
boot loader that would load the
kernel, modules, set tunables, etc prior to
kicking off the kernel.
Between /rescue having all the tools needed
to repair bad updates, repair failing disks, get
that one last backup
before the drive is dead while you wait
for the new drive to be delivered, etc, and
/boot/loader being able to
script loading the kernel while the BIOS
was still around so the need for drivers in the
loader was lessened.
However, as the BIOS evolved into UEFI
and FreeBSD pushed into the embedded space whose
firmware provided a
less rich environment to the boot
loader, so it was able to load things off fewer
and fewer devices, it
became clear that it would need a pivot
root feature to allow it to boot all the way into
FreeBSD, load some
drivers from an included ram disk, and then
use that mount a new root and then
'reroot' to that by killing
everything and running init from that new
root.
FreeBSD also moved from Forth to Lua in its
scripting language for the
boot loader, giving 'pre boot'
environment support better features. I also added
the ability to use
FreeBSD boot loader as a Linux binary
to load FreeBSD and its metadata from a LinuxBoot
environment. Finally,
FreeBSD has 'spun out' and
generalized the /rescue feature to allow creation
of any 'BeastyBox'
environment, similar to what you get
in a busy box, or clone, environment. This
environment, though, is meant
in large part on both Linux and
FreeBSD to be in constrained environments where a
full install is
prohibitive (even those that never pivot
to something more, like ap, routers and nas
boxes).
NetBSD retains many of the old BSD stand-alone programs that started on
the vax.
I've not studied things
beyond noticing this. OpenBSD is similar. Their
boot chain is a bit
simpler than FreeBSD's, though there's
noises about porting FreeBSD's boot there.
There's a port of
/boot/loader to illumos too, but I don't know
if it is the default, or just available. So
I'll not chat about it more.
So the original 'standalone' environment where you had one program
running on a system has evolved
into either a rich boot loader environment that
lets one do a lot to
decide what kernel to load, or towards
having a minimal selection of unix programs
faster and using /bin/sh or
similar to do scripting. These
reduced environments are often called standalone,
though all they share
just the name with the earlier
'stand' programs: they are full unix
programs, but with reduced feature
sets and 'linker magic' to package
them in a way that's faster, smaller, etc
(eg all in one binary).
FreeBSD's boot loader is an outgrowth
of the original standalone env, by way of a port
of NetBSD's libsa.
I suspect in the future, we'll see more and more of a trend for
low-level
init and then handing off to some
built-in kernel (be it Linux, BSD-based
(there's now kexec), or
whatever) to reuse more of the vetted code
rather than re-inventing Unix inside the boot
loader (which is a valid
criticism of FreeBSD's boot loader,
though it's rich feature set is what you get
for the complexity).
Does that answer the prompt? Should I try to make this into more of a
retrospective paper and actually
do the research on the areas I was hand-wavy
about?
Warner