2N/A/* -*-Asm-*- */
2N/A/*
2N/A * GRUB -- GRand Unified Bootloader
2N/A * Copyright (C) 2009 Free Software Foundation, Inc.
2N/A *
2N/A * GRUB is free software: you can redistribute it and/or modify
2N/A * it under the terms of the GNU General Public License as published by
2N/A * the Free Software Foundation, either version 3 of the License, or
2N/A * (at your option) any later version.
2N/A *
2N/A * GRUB is distributed in the hope that it will be useful,
2N/A * but WITHOUT ANY WARRANTY; without even the implied warranty of
2N/A * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2N/A * GNU General Public License for more details.
2N/A *
2N/A * You should have received a copy of the GNU General Public License
2N/A * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
2N/A */
2N/A
2N/A#include <grub/machine/boot.h>
2N/A
2N/A .text
2N/A .align 4
2N/A .globl _start
2N/A_start:
2N/A /* OF CIF entry point arrives in %o4 */
2N/Apic_base:
2N/A call boot_continue
2N/A mov %o4, CIF_REG
2N/A
2N/A /* The offsets to these locations are defined by the
2N/A * GRUB_BOOT_MACHINE_foo macros in include/grub/sparc/ieee1275/boot.h,
2N/A * and grub-setup uses this to patch these next three values as needed.
2N/A *
2N/A * The boot_path will be the OF device path of the partition where the
2N/A * rest of the GRUB kernel image resides. kernel_sector will be set to
2N/A * the location of the first block of the GRUB kernel, and
2N/A * kernel_address is the location where we should load that first block.
2N/A *
2N/A * After loading in that block we will execute it by jumping to the
2N/A * load address plus the size of the prepended A.OUT header (32 bytes).
2N/A */
2N/A . = _start + GRUB_BOOT_MACHINE_BOOT_DEVPATH
2N/Aboot_path:
2N/A . = _start + GRUB_BOOT_MACHINE_KERNEL_BYTE
2N/Aboot_path_end:
2N/Akernel_byte: .xword (2 << 9)
2N/Akernel_address: .word GRUB_BOOT_MACHINE_KERNEL_ADDR
2N/A
2N/Aprom_finddev_name: .asciz "finddevice"
2N/Aprom_chosen_path: .asciz "/chosen"
2N/Aprom_getprop_name: .asciz "getprop"
2N/Aprom_stdout_name: .asciz "stdout"
2N/Aprom_write_name: .asciz "write"
2N/Aprom_bootpath_name: .asciz "bootpath"
2N/Aprom_open_name: .asciz "open"
2N/Aprom_seek_name: .asciz "seek"
2N/Aprom_read_name: .asciz "read"
2N/Aprom_exit_name: .asciz "exit"
2N/Agrub_name: .asciz "GRUB "
2N/A#define GRUB_NAME_LEN 5
2N/A
2N/A .align 4
2N/A
2N/Aprom_open_error:
2N/A GET_ABS(prom_open_name, %o2)
2N/A call console_write
2N/A mov 4, %o3
2N/A /* fallthru */
2N/A
2N/Aprom_error:
2N/A GET_ABS(prom_exit_name, %o0)
2N/A /* fallthru */
2N/A
2N/A /* %o0: OF call name
2N/A * %o1: input arg 1
2N/A */
2N/Aprom_call_1_1_o2:
2N/A clr %o2
2N/A ba prom_call_x_1
2N/A mov 1, %g1
2N/A
2N/Aprom_call_getprop:
2N/A mov 4, %g1
2N/A stx %g1, [%l1 + 256]
2N/A mov CHOSEN_NODE_REG, %o1
2N/A ba prom_call_x_1
2N/A GET_ABS(prom_getprop_name, %o0)
2N/A
2N/Aprom_call_3_1_o1:
2N/A ba prom_call_3_1
2N/A mov BOOTDEV_REG, %o1
2N/A
2N/A
2N/A /* %o2: message string
2N/A * %o3: message length
2N/A */
2N/Aconsole_write:
2N/A GET_ABS(prom_write_name, %o0)
2N/A mov STDOUT_NODE_REG, %o1
2N/A /* fallthru */
2N/A
2N/A /* %o0: OF call name
2N/A * %o1: input arg 1
2N/A * %o2: input arg 2
2N/A * %o3: input arg 3
2N/A */
2N/Aprom_call_3_1:
2N/A mov 3, %g1
2N/Aprom_call_x_1:
2N/A mov 1, %o5
2N/A /* fallthru */
2N/A
2N/A /* %o0: OF call name
2N/A * %g1: num inputs
2N/A * %o5: num outputs
2N/A * %o1-%o4: inputs
2N/A */
2N/Aprom_call:
2N/A stx %o0, [%l1 + 0x00]
2N/A stx %g1, [%l1 + 0x08]
2N/A stx %o5, [%l1 + 0x10]
2N/A stx %o1, [%l1 + 0x18]
2N/A stx %o2, [%l1 + 0x20]
2N/A stx %o3, [%l1 + 0x28]
2N/A stx %o4, [%l1 + 0x30]
2N/A jmpl CIF_REG, %g0
2N/A mov %l1, %o0
2N/A
2N/Aboot_continue:
2N/A mov %o7, PIC_REG /* PIC base */
2N/A sethi %hi(SCRATCH_PAD_BOOT), %l1 /* OF argument slots */
2N/A
2N/A /* Find the /chosen node so we can fetch the stdout handle,
2N/A * and thus perform console output.
2N/A *
2N/A * chosen_node = prom_finddevice("/chosen")
2N/A */
2N/A GET_ABS(prom_finddev_name, %o0)
2N/A call prom_call_1_1_o2
2N/A GET_ABS(prom_chosen_path, %o1)
2N/A
2N/A ldx [%l1 + 0x20], CHOSEN_NODE_REG
2N/A brz CHOSEN_NODE_REG, prom_error
2N/A
2N/A /* getprop(chosen_node, "stdout", &buffer, buffer_size) */
2N/A GET_ABS(prom_stdout_name, %o2)
2N/A add %l1, 256, %o3
2N/A call prom_call_getprop
2N/A mov 1024, %o4
2N/A
2N/A lduw [%l1 + 256], STDOUT_NODE_REG
2N/A brz,pn STDOUT_NODE_REG, prom_error
2N/A
2N/A /* write(stdout_node, "GRUB ", strlen("GRUB ")) */
2N/A GET_ABS(grub_name, %o2)
2N/A call console_write
2N/A mov GRUB_NAME_LEN, %o3
2N/A
2N/A GET_ABS(boot_path, %o3)
2N/A ldub [%o3], %o1
2N/A brnz,pn %o1, bootpath_known
2N/A
2N/A /* getprop(chosen_node, "bootpath", &buffer, buffer_size) */
2N/A GET_ABS(prom_bootpath_name, %o2)
2N/A call prom_call_getprop
2N/A mov (boot_path_end - boot_path), %o4
2N/A
2N/Abootpath_known:
2N/A
2N/A /* Open up the boot_path, and use that handle to read the
2N/A * first block of the GRUB kernel image.
2N/A *
2N/A * bootdev_handle = open(boot_path)
2N/A */
2N/A GET_ABS(prom_open_name, %o0)
2N/A call prom_call_1_1_o2
2N/A GET_ABS(boot_path, %o1)
2N/A
2N/A ldx [%l1 + 0x20], BOOTDEV_REG
2N/A brz,pn BOOTDEV_REG, prom_open_error
2N/A
2N/A /* Since we have 64-bit cells, the high cell of the seek offset
2N/A * is zero and the low cell is the entire value.
2N/A *
2N/A * seek(bootdev, 0, *kernel_byte)
2N/A */
2N/A GET_ABS(prom_seek_name, %o0)
2N/A clr %o2
2N/A call prom_call_3_1_o1
2N/A LDX_ABS(kernel_byte, 0x00, %o3)
2N/A
2N/A /* read(bootdev, *kernel_address, 512) */
2N/A GET_ABS(prom_read_name, %o0)
2N/A LDUW_ABS(kernel_address, 0x00, %o2)
2N/A call prom_call_3_1_o1
2N/A mov 512, %o3
2N/A
2N/A LDUW_ABS(kernel_address, 0x00, %o2)
2N/A jmpl %o2, %o7
2N/A nop
2N/A
2N/A . = _start + GRUB_BOOT_MACHINE_CODE_END
2N/A
2N/A/* the last 4 bytes in the sector 0 contain the signature */
2N/A .word GRUB_BOOT_MACHINE_SIGNATURE