Booting Linux with ARC on SGI Visual Workstations

Bent Hagemark
July 21, 1999

This sketches out how the Linux kernel boots on SGI Visual Workstations based on the ARC boot firmware and how arguments (such as root device) are passed from the ARC boot firmware via a bootloader to the kernel.

The details described here refer to the Visual Workstation Linux kernel patch of 15jul99 and the "arclx.exe" bootloader of 21jul99. The details here probably won't much sense unless you're reading along in that code.


Short form

  1. firmware finds fat-format boot paritition
  2. firmware reads arclx.exe from boot partition
  3. arclx.exe is started with arguments specifying:
  4. arclx.exe reads kernel file into RAM
  5. arclx.exe squirrels away load options in magic ram location
  6. arclx.exe starts kernel
  7. kernel sets GDT to "virtual"
  8. kernel discovers args in magic ram location (including cmd line: root=)
  9. kernel sets internal variables (ROOT_DEV)
  10. kernel proceeds to start up as normal (mounts root device, runs init, etc)

Long form

arclx.exe startup

See arclx.c for the _start() entry point. This is where the boot firmware jumps after loading arclx.exe into memory.

At this point we are already in 32bit protected mode! The GDT is set to "direct-map" (like MIPS k0seg) memory... that is, all memory access is to the physical address (i.e., 0x90000 is the 0x90000'th byte of physical memory, etc).

In ProcessArguments() we look for:

In LoadKernel() we open OSLoadPartition/OSLoadFilename and read an ELF-sized header to make sure it's an ELF file.

Note the file_open() and file_seek_read() routines which abstract firmware level file access. The implementation of these in io.c first tries to open the partition as EXT2FS and falls back to try ARC routines if that fails. The ARC firmware knows how to read FAT and ISO9660 on floppy, IDE and SCSI devices.

LoadProgramSegments() reads in the kernel text and data and clears the BSS region.

How do we know where to put the kernel in RAM? The ELF header shows the kernel is linked to 0xc000000 and we don't have 3 GB of RAM in this machine. The answer: we have a 0xc0000000 baked into arclx.exe and we subtract that of the value in the ELF header to properly land the kernel at 0x100000 physical (where the GDT in the kernel startup expects to map 0xC0100000). Tricky stuff. More to come!

See run.S for the implementation of StartKernel().

arclx.exe kernel arguments

Still more magical things happen here in run.S.

First, the magic 0x90000 block of kernel parameters is initialized -- a magic number is scribbled at 0x90020 (CL_MAGIC_ADDR).

Then, the string of OSLoadOptions is copied to 0x90024 and the offset to this string from the base of the parameter block (CL_BASE_ADDR) is stored in 0x90022 (CL_OFFSET).

To spoil the surprise on this stuff peruse arch/i386/kernel/head.S looking for CL_.

Then, we set the cpu b register to indicate that we're the boot cpu (as opposed to a secondary cpu on a SMP box... see trampoline.S).

Finally... we jump to the kernel address... note that we're jumping there in "physical" mapping space.

kernel startup_32

Some awful CONFIG_VISWS magic in arch/i386/kernel/head.S at startup_32 sets the GDT to the mappings used by the kernel (maps 0xC0000000 -> 0x00000000). (We're already in 32bit protected mode here while generic PCs are still in 16bit mode, but all the normal head.S code works fine if the CPU's GDT is set up properly).

Searching for CL_ we see some code that copies stuff at 0x90000 to empty_zero_page. Two kinds of things are copied by this code: 1) BIOS/LILO parameters [NOT USED by Visual Workstation Linux!], and 2) command line [OSLoadPartition from ARC Boot Selection].

Fast-forward into init/main.c:start_kernel() and then to setup_arch().


Now we're in arch/i386/kernel/setup.c and we see the CONFIG_VISWS code go off to fine just what specific motherboard type and revision we're on. (This is where we can tell a 320 from a 540 or the h/w is pre-production or factory-made).

Then we hardwire memory to 128MB (the smallest support mem config) and ignore _almost_ all things at empty_zero_page (see #define PARAM and note the #ifndef CONFIG_VISWS). The only thing NOT ignored is the command line which we proceed to parse in the normal (legacy PC) manner -- here and in parse_options()).

Note how we take some physical memory for use as the frame buffer.

Fast-forward to parse_options() called back in start_kernel().


This runs as normal... no VISWS-specific hacks here

One thing to note that this is THE way we set the ROOT_DEV variable (from the "root=" command line arg).

Another important variable used here for standalone install is "load_ramdisk=". See how rd_load() is called in drivers/block/genhd.c:device_setup. And, note that we IGNORE the ramdisk related stuff in the PARAM block back in arch/i386/kernel/setup.c.