From: phcoder <phcoder@gmail.com>
To: The development of GRUB 2 <grub-devel@gnu.org>
Subject: Re: GRUB device names wrt. ieee1275
Date: Sun, 22 Mar 2009 01:48:14 +0100 [thread overview]
Message-ID: <49C58ACE.6020309@gmail.com> (raw)
In-Reply-To: <49C58950.90104@gmail.com>
phcoder wrote:
> Device name in boot block will anyway be written in it's complete raw
> OFW form, no matter how the rest of the grub2 accessing the disks. Using
> UUIDs in this place would be nice. On IRC you said that we have 120
> bytes for the name. UUID is 16 or 32 bytes depending on FS. Is it
> possible to fit something like <read first sector> of every disk,
> <compare data at given offset>?
Could we use label checksum for finding right disk?
> I don't agree that all devices should be accessed exclusively by UUID.
> It makes things difficult to maintain because of internal naming. IMO
> quoting and escaping offers the best universal solution. I don't see why
> sth like
> (/pci@1e\,600000/pci@0/pci@9/pci@0/scsi@1/disk@0,1)
> is a problem. Anyway you don't type such a thing manually and probably
> use tab-completition instead
>
>
> David Miller wrote:
>> From: David Miller <davem@davemloft.net>
>> Date: Wed, 18 Mar 2009 13:55:22 -0700 (PDT)
>>
>>> From: Robert Millan <rmh@aybabtu.com>
>>> Date: Wed, 18 Mar 2009 11:18:46 +0100
>>>
>>>> On Sun, Mar 15, 2009 at 03:41:16PM -0700, David Miller wrote:
>>>>> From: Robert Millan <rmh@aybabtu.com>
>>>>> Date: Sun, 15 Mar 2009 16:45:31 +0100
>>>>>
>>>>>> It's not an absolute must that device names are unique. You can
>>>>>> still
>>>>>> identify partitions by their filesystem UUID or label, and in fact
>>>>>> this
>>>>>> is what our default scripts (grub-mkconfig) do anyway.
>>>>> Things like UUID and labels are not an option for the 512-byte
>>>>> boot block where I have to know the exact OBP path of the boot
>>>>> device, and this is what the GRUB kernel fetches from the
>>>>> 'bootpath' environment variable to compose the root device
>>>>> and path.
>>>> What 512-byte boot block?
>>> The one I add in my sparc64 support changes.
>>>
>>> Included here for reference:
>>
>> BTW, I explained exactly how this 512 byte boot block works, in
>> extreme detail, when I first posted my grub sparc64 patches.
>>
>> I am including it here _again_, please read it, it's informative,
>> I promise, and I didn't post it the first time for my health!
>>
>> Thanks!
>>
>> --------------------
>>
>> The first task is to get the first stage boot block going. There are
>> two choices on how to do this. When you type "boot" for a block
>> device, the firmware loads 7.5K starting at the second 512-byte
>> block. If this block device is the third partition (the "all disk"
>> partition in the Sun disk label) this bootblock starts after the disk
>> label.
>>
>> Under Linux we really can only use 512 bytes of that boot block area,
>> because it's possible for the filesystem superblock to show up as
>> early as the very next 512 byte block.
>>
>> The firmware lets you put in the block some sparc executable image
>> (tried by the firmware as 64-bit ELF, then 32-bit ELF, and finally as
>> A.OUT) or tokenized forth. Since 1) we only have 512 bytes and 2)
>> there are no fully GPL'd forth tokenizer implementations (the openbios
>> folks use something that is BSD and MIT licensed) we'll need to go the
>> sparc image route.
>>
>> The task of this 512 byte sequence of code is to load the next stage
>> of the bootloader. For GRUB I've choosen a multi-tiered scheme similar
>> to how the x86 stuff works. The first stage bootloader loads a single
>> block from the disk and jumps to it. This block we load is actually a
>> header block of the main boot loader binary, a second stage loader,
>> which loads the rest of the image.
>>
>> This first stage loader therefore needs a few parameters. It needs to
>> know the OF device path where the second stage header block
>> resides. It needs to know the block to read, and finally it needs to
>> know where to load that block and thus where to jump to it for
>> execution.
>>
>> We'd also like to print some status messages to the screen while this
>> happens and have at least some minimal error handling. Not a small
>> feat in 512 bytes.
>>
>> We put everything in the text section, and the first thing we do is
>> jump over our embedded data bits:
>>
>> .text
>> .align 4
>> .globl _start
>> _start:
>> /* OF CIF entry point arrives in %o4 */
>> pic_base:
>> call boot_continue
>> mov %o4, CIF_REG
>>
>> The "CIF" is the client interface to openfirmware. Calls are made by
>> initializing an array of cells (64-bits on sparc64) in memory which
>> describe the call to be made, the input arguments, and the return
>> values (if any). This value provided in %o4 is the OF entry point we
>> jump to when making calls. The only register argument goes in %o0 and
>> is the base of the aforementioned array of cells.
>>
>> The offsets into the bits coming up are defined in a GRUB boot.h
>> header file so that tools can patch in values during bootblock
>> installation.
>>
>> . = _start + GRUB_BOOT_MACHINE_VER_MAJ
>> boot_version: .byte GRUB_BOOT_VERSION_MAJOR,
>> GRUB_BOOT_VERSION_MINOR
>> boot_path:
>> . = _start + GRUB_BOOT_MACHINE_KERNEL_ADDRESS
>> kernel_sector: .xword 2
>> kernel_address: .word GRUB_BOOT_MACHINE_KERNEL_ADDR
>>
>> The boot_version is just a version blob that various tools and
>> sub-bootloaders could validate for compatibility if they wanted to. It
>> is unused currently.
>>
>> The boot_path will be filled in by the boot block installation tools
>> with the boot device OF path. kernel_sector and kernel_address tell
>> where to load the image from the device into memory. Next, we have
>> string constants we'll need to make OF calls and put messages onto the
>> console:
>>
>> prom_finddev_name: .asciz "finddevice"
>> prom_chosen_path: .asciz "/chosen"
>> prom_getprop_name: .asciz "getprop"
>> prom_stdout_name: .asciz "stdout"
>> prom_write_name: .asciz "write"
>> prom_bootpath_name: .asciz "bootpath"
>> prom_open_name: .asciz "open"
>> prom_seek_name: .asciz "seek"
>> prom_read_name: .asciz "read"
>> prom_exit_name: .asciz "exit"
>> grub_name: .asciz "GRUB "
>>
>> To simplify things we'll write all of our code as position
>> independent. There are other macros in the boot.h header which
>> describe these register name macros such as CIF_REG, PIC_REG,
>> etc. most are local registers which are not volatils across OF calls,
>> so we can keep stable values in them. It also defines macros to
>> compute absolute addresses of a symbol using this PIC_REG, into a
>> register.
>>
>> The next chunk of code are helpers for doing OF calls with various
>> sets of input and output arguments.
>>
>> prom_open_error:
>> GET_ABS(prom_open_name, %o2)
>> call console_write
>> mov 4, %o3
>> /* fallthru */
>>
>> prom_error:
>> GET_ABS(prom_exit_name, %o0)
>> /* fallthru */
>>
>> /* %o0: OF call name
>> * %o1: input arg 1
>> */
>> prom_call_1_1:
>> mov 1, %g1
>> ba prom_call
>> mov 1, %o5
>>
>> /* %o2: message string
>> * %o3: message length
>> */
>> console_write:
>> GET_ABS(prom_write_name, %o0)
>> mov STDOUT_NODE_REG, %o1
>> /* fallthru */
>>
>> /* %o0: OF call name
>> * %o1: input arg 1
>> * %o2: input arg 2
>> * %o3: input arg 3
>> */
>> prom_call_3_1:
>> mov 3, %g1
>> mov 1, %o5
>> /* fallthru */
>>
>> /* %o0: OF call name
>> * %g1: num inputs
>> * %o5: num outputs
>> * %o1-%o4: inputs
>> */
>> prom_call:
>> stx %o0, [%l1 + 0x00]
>> stx %g1, [%l1 + 0x08]
>> stx %o5, [%l1 + 0x10]
>> stx %o1, [%l1 + 0x18]
>> stx %o2, [%l1 + 0x20]
>> stx %o3, [%l1 + 0x28]
>> stx %o4, [%l1 + 0x30]
>> jmpl CIF_REG, %g0
>> mov %l1, %o0
>>
>> All of those routines are "call" invoked, and we do the CIF OF call
>> using "jmpl" with no return register write in order to make the CIF OF
>> call return to the stub caller, ie. a tail call. This way we don't
>> need to allocate a register window here or anything complicated like
>> that.
>>
>> Now for the code we jumped to at the _start header. Our first task is
>> to save away the PIC_REG (%o7 was set by the initial "call"
>> instruction, and will be equal to _start, we use it to reference our
>> data blobs PC relative). Also we setup register %l1 which holds the
>> base of the OF cell argument data block used above. SCRATCH_PAD is
>> defined to 0x10000, which is guarenteed to be mapped by OF and outside
>> of where we will be executing (which is at 0x4000).
>>
>> boot_continue:
>> mov %o7, PIC_REG /* PIC base */
>> sethi %hi(SCRATCH_PAD), %l1 /* OF argument slots */
>>
>> We need the console stdout handle to write console messages, this is
>> done by finding the "/chosen" directory in the OF device tree, and
>> fetching from there the "stdout" property which is a 32-bit handle.
>>
>> GET_ABS(prom_finddev_name, %o0)
>> GET_ABS(prom_chosen_path, %o1)
>> call prom_call_1_1
>> clr %o2
>> ldx [%l1 + 0x20], CHOSEN_NODE_REG
>> brz CHOSEN_NODE_REG, prom_error
>> GET_ABS(prom_getprop_name, %o0)
>> mov 4, %g1
>> mov 1, %o5
>> mov CHOSEN_NODE_REG, %o1
>> GET_ABS(prom_stdout_name, %o2)
>> add %l1, 256, %o3
>> mov 1024, %o4
>> call prom_call
>> stx %g1, [%l1 + 256]
>> lduw [%l1 + 256], STDOUT_NODE_REG
>> brz,pn STDOUT_NODE_REG, prom_error
>>
>> Since we have very little space, the error handling has to be
>> small. The "clr %o2" in the prom_call_1_1 invocation causes the return
>> value cell slot (at %l1 + 0x20) to be cleared by the prom_call
>> code. Zero is not a prom handle we'll get back, so if the slot stays
>> zero we took an error. In the getprop call to get the 'stdout' handle,
>> we rely upon OF providing us with zero'd memory outside of the boot
>> block image.
>>
>> Now that we have the console output cookie, we can print out a message:
>>
>> GET_ABS(grub_name, %o2)
>> call console_write
>> mov 5, %o3
>>
>> Next, we open up the boot device:
>>
>> GET_ABS(prom_open_name, %o0)
>> GET_ABS(boot_path, %o1)
>> call prom_call_1_1
>> clr %o2
>>
>> ldx [%l1 + 0x20], BOOTDEV_REG
>> brz,pn BOOTDEV_REG, prom_open_error
>>
>> We now seek to the appropriate block:
>>
>> GET_ABS(prom_seek_name, %o0)
>> mov BOOTDEV_REG, %o1
>> clr %o2
>> LDX_ABS(kernel_sector, 0x00, %o3)
>> call prom_call_3_1
>> sllx %o3, 9, %o3
>>
>> and finally read the block into memory:
>>
>> GET_ABS(prom_read_name, %o0)
>> mov BOOTDEV_REG, %o1
>> LDUW_ABS(kernel_address, 0x00, %o2)
>> call prom_call_3_1
>> mov 512, %o3
>>
>> This image will be an A.OUT image as well, so we jump into it right
>> past the A.OUT header:
>>
>> LDUW_ABS(kernel_address, 0x00, %o2)
>> jmpl %o2 + GRUB_BOOT_AOUT_HEADER_SIZE, %o7
>> nop
>> 1: ba,a 1b
>>
>> Just for fun we put an endless loop after the jump in case it does
>> return for some reason. We could, alternatively, call prom_error
>> instead. Now skip to 4 bytes right before the end of the 512-byte
>> block and write a signature cookie.
>>
>> . = _start + GRUB_BOOT_MACHINE_CODE_END
>>
>> /* the last 4 bytes in the sector 0 contain the signature */
>> .word GRUB_BOOT_MACHINE_SIGNATURE
>>
>> And that's the whole boot block implementation. This file is compiled
>> normally into first an ELF image using GCC. Then we strip out all the
>> symbols and other junk, and finally use objcopy to output an A.OUT
>> object. It is exactly 512 bytes in size and the bootblock installer
>> verifies this.
>>
>> The second stage header block code now can take advantage of all of
>> the values the first stage has computed, such as the stdout handle,
>> the handle for the boot device, etc. In the second stage image, it
>> begins with assembler just like the first stage does above. But, it
>> implements a block + length tuple list which grows down from the end
>> of the block. This tells us where to read in the rest of the GRUB
>> kernel from the actual file on the disk.
>>
>> The GRUB installer program links in with modules that understand how
>> various filesystems work. It has a block traverser that can be run on
>> arbitrary files. This is the mechanism used to build the block list
>> which is patched into this second stage loader block list.
>>
>> I've tested both of these using a Sun LDOM guest, which is the fastest
>> way to test low-level stuff like this since resetting (which you need
>> to do every test boot attempt) is nearly instantaneous.
>>
>> The current task is to flesh out the installer programs and make sure
>> the rest of the GRUB ieee1275 code is going to work properly on
>> sparc. I already know some bits that will need some tweaking. For
>> example, partitions on OF paths are indicated (aparently) by adding
>> ":N" where N is a partition number. On sparc this "N" is instead a
>> letter.
>>
>>
>> _______________________________________________
>> Grub-devel mailing list
>> Grub-devel@gnu.org
>> http://lists.gnu.org/mailman/listinfo/grub-devel
>
>
--
Regards
Vladimir 'phcoder' Serbinenko
next prev parent reply other threads:[~2009-03-22 0:48 UTC|newest]
Thread overview: 18+ messages / expand[flat|nested] mbox.gz Atom feed top
2009-03-14 22:14 GRUB device names wrt. ieee1275 David Miller
2009-03-14 23:44 ` phcoder
2009-03-15 5:24 ` David Miller
2009-03-15 9:22 ` Vesa Jääskeläinen
2009-03-15 22:52 ` David Miller
2009-03-15 15:45 ` Robert Millan
2009-03-15 22:41 ` David Miller
2009-03-18 10:18 ` Robert Millan
2009-03-18 20:55 ` David Miller
2009-03-18 21:01 ` David Miller
2009-03-22 0:41 ` phcoder
2009-03-22 0:48 ` phcoder [this message]
2009-03-22 1:57 ` David Miller
2009-03-22 1:56 ` David Miller
[not found] ` <49C61E3D.3040901@gmail.com>
[not found] ` <20090322.153022.233646325.davem@davemloft.net>
2009-03-22 22:51 ` phcoder
2009-03-23 1:13 ` David Miller
2009-03-22 12:22 ` Robert Millan
2009-03-23 4:23 ` David Miller
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=49C58ACE.6020309@gmail.com \
--to=phcoder@gmail.com \
--cc=grub-devel@gnu.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.