diff options
Diffstat (limited to 'grub-core/kern/mips')
-rw-r--r-- | grub-core/kern/mips/arc/init.c | 463 | ||||
-rw-r--r-- | grub-core/kern/mips/cache.S | 70 | ||||
-rw-r--r-- | grub-core/kern/mips/cache_flush.S | 54 | ||||
-rw-r--r-- | grub-core/kern/mips/dl.c | 274 | ||||
-rw-r--r-- | grub-core/kern/mips/init.c | 38 | ||||
-rw-r--r-- | grub-core/kern/mips/loongson/init.c | 320 | ||||
-rw-r--r-- | grub-core/kern/mips/qemu_mips/init.c | 105 | ||||
-rw-r--r-- | grub-core/kern/mips/startup.S | 126 |
8 files changed, 1450 insertions, 0 deletions
diff --git a/grub-core/kern/mips/arc/init.c b/grub-core/kern/mips/arc/init.c new file mode 100644 index 0000000..2ed3ff3 --- /dev/null +++ b/grub-core/kern/mips/arc/init.c @@ -0,0 +1,463 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2009,2010 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <grub/kernel.h> +#include <grub/misc.h> +#include <grub/env.h> +#include <grub/time.h> +#include <grub/types.h> +#include <grub/misc.h> +#include <grub/mm.h> +#include <grub/time.h> +#include <grub/machine/kernel.h> +#include <grub/machine/memory.h> +#include <grub/arc/console.h> +#include <grub/cpu/memory.h> +#include <grub/cpu/time.h> +#include <grub/memory.h> +#include <grub/term.h> +#include <grub/arc/arc.h> +#include <grub/offsets.h> +#include <grub/i18n.h> +#include <grub/disk.h> +#include <grub/partition.h> + +const char *type_names[] = { +#ifdef GRUB_CPU_WORDS_BIGENDIAN + NULL, +#endif + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + "eisa", "tc", "scsi", "dti", "multi", "disk", "tape", "cdrom", "worm", + "serial", "net", "video", "par", "point", "key", "audio", "other", + "rdisk", "fdisk", "tape", "modem", "monitor", "print", "pointer", + "keyboard", "term", +#ifndef GRUB_CPU_WORDS_BIGENDIAN + "other", +#endif + "line", "network", NULL +}; + +static int +iterate_rec (const char *prefix, const struct grub_arc_component *parent, + grub_arc_iterate_devs_hook_t hook, void *hook_data, + int alt_names) +{ + const struct grub_arc_component *comp; + FOR_ARC_CHILDREN(comp, parent) + { + char *name; + const char *cname = NULL; + if (comp->type < ARRAY_SIZE (type_names)) + cname = type_names[comp->type]; + if (!cname) + cname = "unknown"; + if (alt_names) + name = grub_xasprintf ("%s/%s%lu", prefix, cname, comp->key); + else + name = grub_xasprintf ("%s%s(%lu)", prefix, cname, comp->key); + if (!name) + return 1; + if (hook (name, comp, hook_data)) + { + grub_free (name); + return 1; + } + if (iterate_rec ((parent ? name : prefix), comp, hook, hook_data, + alt_names)) + { + grub_free (name); + return 1; + } + grub_free (name); + } + return 0; +} + +int +grub_arc_iterate_devs (grub_arc_iterate_devs_hook_t hook, void *hook_data, + int alt_names) +{ + return iterate_rec ((alt_names ? "arc" : ""), NULL, hook, hook_data, + alt_names); +} + +grub_err_t +grub_machine_mmap_iterate (grub_memory_hook_t hook, void *hook_data) +{ + struct grub_arc_memory_descriptor *cur = NULL; + while (1) + { + grub_memory_type_t type; + cur = GRUB_ARC_FIRMWARE_VECTOR->getmemorydescriptor (cur); + if (!cur) + return GRUB_ERR_NONE; + switch (cur->type) + { + case GRUB_ARC_MEMORY_EXCEPTION_BLOCK: + case GRUB_ARC_MEMORY_SYSTEM_PARAMETER_BLOCK: + case GRUB_ARC_MEMORY_FW_PERMANENT: + default: + type = GRUB_MEMORY_RESERVED; + break; + + case GRUB_ARC_MEMORY_FW_TEMPORARY: + case GRUB_ARC_MEMORY_FREE: + case GRUB_ARC_MEMORY_LOADED: + case GRUB_ARC_MEMORY_FREE_CONTIGUOUS: + type = GRUB_MEMORY_AVAILABLE; + break; + case GRUB_ARC_MEMORY_BADRAM: + type = GRUB_MEMORY_BADRAM; + break; + } + if (hook (((grub_uint64_t) cur->start_page) << 12, + ((grub_uint64_t) cur->num_pages) << 12, type, hook_data)) + return GRUB_ERR_NONE; + } +} + +char * +grub_arc_alt_name_to_norm (const char *name, const char *suffix) +{ + char *optr; + const char *iptr; + char * ret = grub_malloc (2 * grub_strlen (name) + grub_strlen (suffix)); + int state = 0; + + if (!ret) + return NULL; + optr = ret; + for (iptr = name + 4; *iptr; iptr++) + if (state == 0) + { + if (!grub_isdigit (*iptr)) + *optr++ = *iptr; + else + { + *optr++ = '('; + *optr++ = *iptr; + state = 1; + } + } + else + { + if (grub_isdigit (*iptr)) + *optr++ = *iptr; + else + { + *optr++ = ')'; + state = 0; + } + } + if (state) + *optr++ = ')'; + grub_strcpy (optr, suffix); + return ret; +} + +static char * +norm_name_to_alt (const char *name) +{ + char *optr; + const char *iptr; + int state = 0; + char * ret = grub_malloc (grub_strlen (name) + sizeof ("arc/")); + + if (!ret) + return NULL; + optr = grub_stpcpy (ret, "arc/"); + for (iptr = name; *iptr; iptr++) + { + if (state == 3) + { + *optr++ = '/'; + state = 0; + } + if (*iptr == '(') + { + state = 1; + continue; + } + if (*iptr == ')') + { + if (state == 1) + *optr++ = '0'; + state = 3; + continue; + } + *optr++ = *iptr; + if (state == 1) + state = 2; + } + *optr = '\0'; + return ret; +} + +extern grub_uint32_t grub_total_modules_size __attribute__ ((section(".text"))); +grub_addr_t grub_modbase; + +extern char _end[]; +static char boot_location[256]; + +void +grub_machine_init (void) +{ + struct grub_arc_memory_descriptor *cur = NULL; + grub_addr_t modend; + + grub_memcpy (boot_location, + (char *) (GRUB_DECOMPRESSOR_LINK_ADDR - 256), 256); + + grub_modbase = ALIGN_UP ((grub_addr_t) _end, GRUB_KERNEL_MACHINE_MOD_ALIGN); + modend = grub_modbase + grub_total_modules_size; + grub_console_init_early (); + + /* FIXME: measure this. */ + grub_arch_cpuclock = 150000000; + grub_install_get_time_ms (grub_rtc_get_time_ms); + + while (1) + { + grub_uint64_t start, end; + cur = GRUB_ARC_FIRMWARE_VECTOR->getmemorydescriptor (cur); + if (!cur) + break; + if (cur->type != GRUB_ARC_MEMORY_FREE + && cur->type != GRUB_ARC_MEMORY_LOADED + && cur->type != GRUB_ARC_MEMORY_FREE_CONTIGUOUS) + continue; + start = ((grub_uint64_t) cur->start_page) << 12; + end = ((grub_uint64_t) cur->num_pages) << 12; + end += start; + if ((grub_uint64_t) start < (modend & 0x1fffffff)) + start = (modend & 0x1fffffff); + if ((grub_uint64_t) end > 0x20000000) + end = 0x20000000; + if (end > start) + grub_mm_init_region ((void *) (grub_addr_t) (start | 0x80000000), + end - start); + } + + grub_console_init_lately (); + + grub_arcdisk_init (); +} + +void +grub_machine_fini (int flags __attribute__ ((unused))) +{ +} + +void +grub_halt (void) +{ + GRUB_ARC_FIRMWARE_VECTOR->powerdown (); + + grub_millisleep (1500); + + grub_puts_ (N_("Shutdown failed")); + grub_refresh (); + while (1); +} + +void +grub_exit (void) +{ + GRUB_ARC_FIRMWARE_VECTOR->exit (); + + grub_millisleep (1500); + + grub_puts_ (N_("Exit failed")); + grub_refresh (); + while (1); +} + +static char * +get_part (char *dev) +{ + char *ptr; + if (!*dev) + return 0; + ptr = dev + grub_strlen (dev) - 1; + if (ptr == dev || *ptr != ')') + return 0; + ptr--; + while (grub_isdigit (*ptr) && ptr > dev) + ptr--; + if (*ptr != '(' || ptr == dev) + return 0; + ptr--; + if (ptr - dev < (int) sizeof ("partition") - 2) + return 0; + ptr -= sizeof ("partition") - 2; + if (grub_memcmp (ptr, "partition", sizeof ("partition") - 1) != 0) + return 0; + return ptr; +} + +static grub_disk_addr_t +get_partition_offset (char *part, grub_disk_addr_t *en) +{ + grub_arc_fileno_t handle; + grub_disk_addr_t ret = -1; + struct grub_arc_fileinfo info; + grub_arc_err_t r; + + if (GRUB_ARC_FIRMWARE_VECTOR->open (part, GRUB_ARC_FILE_ACCESS_OPEN_RO, + &handle)) + return -1; + + r = GRUB_ARC_FIRMWARE_VECTOR->getfileinformation (handle, &info); + if (!r) + { + ret = (info.start >> 9); + *en = (info.end >> 9); + } + GRUB_ARC_FIRMWARE_VECTOR->close (handle); + return ret; +} + +struct get_device_name_ctx +{ + char *partition_name; + grub_disk_addr_t poff, pend; +}; + +static int +get_device_name_iter (grub_disk_t disk __attribute__ ((unused)), + const grub_partition_t part, void *data) +{ + struct get_device_name_ctx *ctx = data; + + if (grub_partition_get_start (part) == ctx->poff + && grub_partition_get_len (part) == ctx->pend) + { + ctx->partition_name = grub_partition_get_name (part); + return 1; + } + + return 0; +} + +void +grub_machine_get_bootlocation (char **device, char **path) +{ + char *loaddev = boot_location; + char *pptr, *partptr; + char *dname; + grub_disk_addr_t poff = -1, pend = -1; + struct get_device_name_ctx ctx; + grub_disk_t parent = 0; + unsigned i; + + for (i = 0; i < ARRAY_SIZE (type_names); i++) + if (type_names[i] + && grub_memcmp (loaddev, type_names[i], grub_strlen (type_names[i])) == 0 + && loaddev[grub_strlen (type_names[i])] == '(') + break; + if (i == ARRAY_SIZE (type_names)) + pptr = loaddev; + else + for (pptr = loaddev; *pptr && *pptr != '/' && *pptr != '\\'; pptr++); + if (*pptr) + { + char *iptr, *optr; + char sep = *pptr; + *path = grub_malloc (grub_strlen (pptr) + 1); + if (!*path) + return; + for (iptr = pptr, optr = *path; *iptr; iptr++, optr++) + if (*iptr == sep) + *optr = '/'; + else + *optr = *iptr; + *optr = '\0'; + *path = grub_strdup (pptr); + *pptr = '\0'; + } + + if (*loaddev == '\0') + { + const char *syspart = 0; + + if (GRUB_ARC_SYSTEM_PARAMETER_BLOCK->firmware_vector_length + >= (unsigned) ((char *) (&GRUB_ARC_FIRMWARE_VECTOR->getenvironmentvariable + 1) + - (char *) GRUB_ARC_FIRMWARE_VECTOR) + && GRUB_ARC_FIRMWARE_VECTOR->getenvironmentvariable) + syspart = GRUB_ARC_FIRMWARE_VECTOR->getenvironmentvariable ("SystemPartition"); + if (!syspart) + return; + loaddev = grub_strdup (syspart); + } + + partptr = get_part (loaddev); + if (partptr) + { + poff = get_partition_offset (loaddev, &pend); + *partptr = '\0'; + } + dname = norm_name_to_alt (loaddev); + if (poff == (grub_addr_t) -1) + { + *device = dname; + if (loaddev != boot_location) + grub_free (loaddev); + return; + } + + parent = grub_disk_open (dname); + if (!parent) + { + *device = dname; + if (loaddev != boot_location) + grub_free (loaddev); + return; + } + + if (poff == 0 + && pend == grub_disk_native_sectors (parent)) + { + grub_disk_close (parent); + *device = dname; + if (loaddev != boot_location) + grub_free (loaddev); + return; + } + + ctx.partition_name = NULL; + ctx.poff = poff; + ctx.pend = pend; + + grub_partition_iterate (parent, get_device_name_iter, &ctx); + grub_disk_close (parent); + + if (! ctx.partition_name) + { + *device = dname; + if (loaddev != boot_location) + grub_free (loaddev); + return; + } + + *device = grub_xasprintf ("%s,%s", dname, + ctx.partition_name); + grub_free (ctx.partition_name); + grub_free (dname); + if (loaddev != boot_location) + grub_free (loaddev); +} diff --git a/grub-core/kern/mips/cache.S b/grub-core/kern/mips/cache.S new file mode 100644 index 0000000..fa331ec --- /dev/null +++ b/grub-core/kern/mips/cache.S @@ -0,0 +1,70 @@ + +#include <grub/symbol.h> + + .set noreorder + .set nomacro + +FUNCTION (grub_arch_sync_caches) +#include "cache_flush.S" + j $ra + nop + +FUNCTION (grub_arch_sync_dma_caches) + move $t2, $a0 + addu $t3, $a0, $a1 + srl $t2, $t2, 5 + sll $t2, $t2, 5 + addu $t3, $t3, 0x1f + srl $t3, $t3, 5 + sll $t3, $t3, 5 + move $t0, $t2 + subu $t1, $t3, $t2 +1: + cache_op 1, 0($t0) +#ifdef GRUB_MACHINE_MIPS_LOONGSON + cache_op 1, 1($t0) + cache_op 1, 2($t0) + cache_op 1, 3($t0) + + addiu $t1, $t1, -0x20 + bne $t1, $zero, 1b + addiu $t0, $t0, 0x20 +#else + addiu $t1, $t1, -4 + bne $t1, $zero, 1b + addiu $t0, $t0, 0x4 +#endif + sync_op + move $t0, $t2 + subu $t1, $t3, $t2 +2: +#ifdef GRUB_MACHINE_MIPS_LOONGSON + cache_op 0, 0($t0) + addiu $t1, $t1, -0x20 + bne $t1, $zero, 2b + addiu $t0, $t0, 0x20 +#else + cache_op 0, 0($t0) + addiu $t1, $t1, -4 + bne $t1, $zero, 2b + addiu $t0, $t0, 0x4 +#endif + sync_op + move $t0, $t2 + subu $t1, $t3, $t2 +2: +#ifdef GRUB_MACHINE_MIPS_LOONGSON + cache_op 23, 0($t0) + addiu $t1, $t1, -0x20 + bne $t1, $zero, 2b + addiu $t0, $t0, 0x20 +#else + cache_op 23, 0($t0) + addiu $t1, $t1, -0x4 + bne $t1, $zero, 2b + addiu $t0, $t0, 0x4 +#endif + sync_op + + jr $ra + nop diff --git a/grub-core/kern/mips/cache_flush.S b/grub-core/kern/mips/cache_flush.S new file mode 100644 index 0000000..89961a0 --- /dev/null +++ b/grub-core/kern/mips/cache_flush.S @@ -0,0 +1,54 @@ +#ifndef CACHE_OP_DEFINED +#define CACHE_OP_DEFINED 1 + .macro cache_op op addr + .set mips3 + cache \op, \addr + .set mips1 + .endm + .macro sync_op + .set mips3 + sync + .set mips1 + .endm +#endif + + move $t2, $a0 + addu $t3, $a0, $a1 + srl $t2, $t2, 5 + sll $t2, $t2, 5 + addu $t3, $t3, 0x1f + srl $t3, $t3, 5 + sll $t3, $t3, 5 + move $t0, $t2 + subu $t1, $t3, $t2 +1: + cache_op 1, 0($t0) + /* All four ways. */ +#ifdef GRUB_MACHINE_MIPS_LOONGSON + cache_op 1, 1($t0) + cache_op 1, 2($t0) + cache_op 1, 3($t0) + addiu $t1, $t1, -0x20 + bne $t1, $zero, 1b + addiu $t0, $t0, 0x20 + +#else + addiu $t1, $t1, -0x4 + bne $t1, $zero, 1b + addiu $t0, $t0, 0x4 +#endif + sync_op + move $t0, $t2 + subu $t1, $t3, $t2 +2: + cache_op 0, 0($t0) +#ifdef GRUB_MACHINE_MIPS_LOONGSON + addiu $t1, $t1, -0x20 + bne $t1, $zero, 2b + addiu $t0, $t0, 0x20 +#else + addiu $t1, $t1, -0x4 + bne $t1, $zero, 2b + addiu $t0, $t0, 0x4 +#endif + sync_op diff --git a/grub-core/kern/mips/dl.c b/grub-core/kern/mips/dl.c new file mode 100644 index 0000000..5d7d299 --- /dev/null +++ b/grub-core/kern/mips/dl.c @@ -0,0 +1,274 @@ +/* dl-386.c - arch-dependent part of loadable module support */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2002,2005,2007,2009 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <grub/dl.h> +#include <grub/elf.h> +#include <grub/misc.h> +#include <grub/err.h> +#include <grub/cpu/types.h> +#include <grub/mm.h> +#include <grub/i18n.h> + +/* Dummy __gnu_local_gp. Resolved by linker. */ +static char __gnu_local_gp_dummy; +static char _gp_disp_dummy; + +/* Check if EHDR is a valid ELF header. */ +grub_err_t +grub_arch_dl_check_header (void *ehdr) +{ + Elf_Ehdr *e = ehdr; + + /* Check the magic numbers. */ +#ifdef GRUB_CPU_WORDS_BIGENDIAN + if (e->e_ident[EI_CLASS] != ELFCLASS32 + || e->e_ident[EI_DATA] != ELFDATA2MSB + || e->e_machine != EM_MIPS) +#else + if (e->e_ident[EI_CLASS] != ELFCLASS32 + || e->e_ident[EI_DATA] != ELFDATA2LSB + || e->e_machine != EM_MIPS) +#endif + return grub_error (GRUB_ERR_BAD_OS, N_("invalid arch-dependent ELF magic")); + + return GRUB_ERR_NONE; +} + +#pragma GCC diagnostic ignored "-Wcast-align" + +grub_err_t +grub_arch_dl_get_tramp_got_size (const void *ehdr, grub_size_t *tramp, + grub_size_t *got) +{ + const Elf_Ehdr *e = ehdr; + const Elf_Shdr *s; + /* FIXME: suboptimal. */ + grub_size_t gp_size = 0; + unsigned i; + + *tramp = 0; + *got = 0; + + for (i = 0, s = (const Elf_Shdr *) ((const char *) e + e->e_shoff); + i < e->e_shnum; + i++, s = (const Elf_Shdr *) ((const char *) s + e->e_shentsize)) + if (s->sh_type == SHT_REL) + { + const Elf_Rel *rel, *max; + + for (rel = (const Elf_Rel *) ((const char *) e + s->sh_offset), + max = rel + s->sh_size / s->sh_entsize; + rel < max; + rel++) + switch (ELF_R_TYPE (rel->r_info)) + { + case R_MIPS_GOT16: + case R_MIPS_CALL16: + case R_MIPS_GPREL32: + gp_size += 4; + break; + } + } + + if (gp_size > 0x08000) + return grub_error (GRUB_ERR_OUT_OF_RANGE, "__gnu_local_gp is too big\n"); + + *got = gp_size; + + return GRUB_ERR_NONE; +} + +/* Relocate symbols. */ +grub_err_t +grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr, + Elf_Shdr *s, grub_dl_segment_t seg) +{ + grub_uint32_t gp0; + Elf_Ehdr *e = ehdr; + + if (!mod->reginfo) + { + unsigned i; + Elf_Shdr *ri; + + /* Find reginfo. */ + for (i = 0, ri = (Elf_Shdr *) ((char *) ehdr + e->e_shoff); + i < e->e_shnum; + i++, ri = (Elf_Shdr *) ((char *) ri + e->e_shentsize)) + if (ri->sh_type == SHT_MIPS_REGINFO) + break; + if (i == e->e_shnum) + return grub_error (GRUB_ERR_BAD_MODULE, "no reginfo found"); + mod->reginfo = (grub_uint32_t *)((char *) ehdr + ri->sh_offset); + } + + gp0 = mod->reginfo[5]; + Elf_Rel *rel, *max; + + for (rel = (Elf_Rel *) ((char *) e + s->sh_offset), + max = (Elf_Rel *) ((char *) rel + s->sh_size); + rel < max; + rel = (Elf_Rel *) ((char *) rel + s->sh_entsize)) + { + grub_uint8_t *addr; + Elf_Sym *sym; + grub_uint32_t sym_value; + + if (seg->size < rel->r_offset) + return grub_error (GRUB_ERR_BAD_MODULE, + "reloc offset is out of the segment"); + + addr = (grub_uint8_t *) ((char *) seg->addr + rel->r_offset); + sym = (Elf_Sym *) ((char *) mod->symtab + + mod->symsize * ELF_R_SYM (rel->r_info)); + sym_value = sym->st_value; + if (s->sh_type == SHT_RELA) + { + sym_value += ((Elf_Rela *) rel)->r_addend; + } + if (sym_value == (grub_addr_t) &__gnu_local_gp_dummy) + sym_value = (grub_addr_t) mod->got; + else if (sym_value == (grub_addr_t) &_gp_disp_dummy) + { + sym_value = (grub_addr_t) mod->got - (grub_addr_t) addr; + if (ELF_R_TYPE (rel->r_info) == R_MIPS_LO16) + /* ABI mandates +4 even if partner lui doesn't + immediately precede addiu. */ + sym_value += 4; + } + switch (ELF_R_TYPE (rel->r_info)) + { + case R_MIPS_HI16: + { + grub_uint32_t value; + Elf_Rel *rel2; + +#ifdef GRUB_CPU_WORDS_BIGENDIAN + addr += 2; +#endif + + /* Handle partner lo16 relocation. Lower part is + treated as signed. Hence add 0x8000 to compensate. + */ + value = (*(grub_uint16_t *) addr << 16) + + sym_value + 0x8000; + for (rel2 = rel + 1; rel2 < max; rel2++) + if (ELF_R_SYM (rel2->r_info) + == ELF_R_SYM (rel->r_info) + && ELF_R_TYPE (rel2->r_info) == R_MIPS_LO16) + { + value += *(grub_int16_t *) + ((char *) seg->addr + rel2->r_offset +#ifdef GRUB_CPU_WORDS_BIGENDIAN + + 2 +#endif + ); + break; + } + *(grub_uint16_t *) addr = (value >> 16) & 0xffff; + } + break; + case R_MIPS_LO16: +#ifdef GRUB_CPU_WORDS_BIGENDIAN + addr += 2; +#endif + *(grub_uint16_t *) addr += sym_value & 0xffff; + break; + case R_MIPS_32: + *(grub_uint32_t *) addr += sym_value; + break; + case R_MIPS_GPREL32: + *(grub_uint32_t *) addr = sym_value + + *(grub_uint32_t *) addr + gp0 - (grub_uint32_t)mod->got; + break; + + case R_MIPS_26: + { + grub_uint32_t value; + grub_uint32_t raw; + raw = (*(grub_uint32_t *) addr) & 0x3ffffff; + value = raw << 2; + value += sym_value; + raw = (value >> 2) & 0x3ffffff; + + *(grub_uint32_t *) addr = + raw | ((*(grub_uint32_t *) addr) & 0xfc000000); + } + break; + case R_MIPS_GOT16: + if (ELF_ST_BIND (sym->st_info) == STB_LOCAL) + { + Elf_Rel *rel2; + /* Handle partner lo16 relocation. Lower part is + treated as signed. Hence add 0x8000 to compensate. + */ + sym_value += (*(grub_uint16_t *) addr << 16) + + 0x8000; + for (rel2 = rel + 1; rel2 < max; rel2++) + if (ELF_R_SYM (rel2->r_info) + == ELF_R_SYM (rel->r_info) + && ELF_R_TYPE (rel2->r_info) == R_MIPS_LO16) + { + sym_value += *(grub_int16_t *) + ((char *) seg->addr + rel2->r_offset +#ifdef GRUB_CPU_WORDS_BIGENDIAN + + 2 +#endif + ); + break; + } + sym_value &= 0xffff0000; + *(grub_uint16_t *) addr = 0; + } + /* Fallthrough. */ + case R_MIPS_CALL16: + { + grub_uint32_t *gpptr = mod->gotptr; + /* FIXME: reuse*/ +#ifdef GRUB_CPU_WORDS_BIGENDIAN + addr += 2; +#endif + *gpptr = sym_value + *(grub_uint16_t *) addr; + *(grub_uint16_t *) addr + = sizeof (grub_uint32_t) * (gpptr - (grub_uint32_t *) mod->got); + mod->gotptr = gpptr + 1; + break; + } + case R_MIPS_JALR: + break; + default: + { + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + N_("relocation 0x%x is not implemented yet"), + ELF_R_TYPE (rel->r_info)); + } + break; + } + } + + return GRUB_ERR_NONE; +} + +void +grub_arch_dl_init_linker (void) +{ + grub_dl_register_symbol ("__gnu_local_gp", &__gnu_local_gp_dummy, 0, 0); + grub_dl_register_symbol ("_gp_disp", &_gp_disp_dummy, 0, 0); +} + diff --git a/grub-core/kern/mips/init.c b/grub-core/kern/mips/init.c new file mode 100644 index 0000000..14b8752 --- /dev/null +++ b/grub-core/kern/mips/init.c @@ -0,0 +1,38 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2009 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <grub/kernel.h> +#include <grub/env.h> +#include <grub/cpu/time.h> +#include <grub/cpu/mips.h> + +/* FIXME: use interrupt to count high. */ +grub_uint64_t +grub_get_rtc (void) +{ + static grub_uint32_t high = 0; + static grub_uint32_t last = 0; + grub_uint32_t low; + + asm volatile ("mfc0 %0, " GRUB_CPU_MIPS_COP0_TIMER_COUNT : "=r" (low)); + if (low < last) + high++; + last = low; + + return (((grub_uint64_t) high) << 32) | low; +} diff --git a/grub-core/kern/mips/loongson/init.c b/grub-core/kern/mips/loongson/init.c new file mode 100644 index 0000000..7b96531 --- /dev/null +++ b/grub-core/kern/mips/loongson/init.c @@ -0,0 +1,320 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2009,2010 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <grub/kernel.h> +#include <grub/misc.h> +#include <grub/env.h> +#include <grub/time.h> +#include <grub/types.h> +#include <grub/misc.h> +#include <grub/mm.h> +#include <grub/machine/time.h> +#include <grub/machine/kernel.h> +#include <grub/machine/memory.h> +#include <grub/memory.h> +#include <grub/mips/loongson.h> +#include <grub/cs5536.h> +#include <grub/term.h> +#include <grub/cpu/memory.h> +#include <grub/i18n.h> +#include <grub/video.h> +#include <grub/terminfo.h> +#include <grub/keyboard_layouts.h> +#include <grub/serial.h> +#include <grub/loader.h> +#include <grub/at_keyboard.h> + +grub_err_t +grub_machine_mmap_iterate (grub_memory_hook_t hook, void *hook_data) +{ + hook (GRUB_ARCH_LOWMEMPSTART, grub_arch_memsize << 20, + GRUB_MEMORY_AVAILABLE, hook_data); + hook (GRUB_ARCH_HIGHMEMPSTART, grub_arch_highmemsize << 20, + GRUB_MEMORY_AVAILABLE, hook_data); + return GRUB_ERR_NONE; +} + +/* Helper for init_pci. */ +static int +set_card (grub_pci_device_t dev, grub_pci_id_t pciid, + void *data __attribute__ ((unused))) +{ + grub_pci_address_t addr; + /* We could use grub_pci_assign_addresses for this but we prefer to + have exactly same memory map as on pmon. */ + switch (pciid) + { + case GRUB_LOONGSON_OHCI_PCIID: + addr = grub_pci_make_address (dev, GRUB_PCI_REG_ADDRESS_REG0); + grub_pci_write (addr, 0x5025000); + addr = grub_pci_make_address (dev, GRUB_PCI_REG_COMMAND); + grub_pci_write_word (addr, GRUB_PCI_COMMAND_SERR_ENABLE + | GRUB_PCI_COMMAND_PARITY_ERROR + | GRUB_PCI_COMMAND_BUS_MASTER + | GRUB_PCI_COMMAND_MEM_ENABLED); + + addr = grub_pci_make_address (dev, GRUB_PCI_REG_STATUS); + grub_pci_write_word (addr, 0x0200 | GRUB_PCI_STATUS_CAPABILITIES); + break; + case GRUB_LOONGSON_EHCI_PCIID: + addr = grub_pci_make_address (dev, GRUB_PCI_REG_ADDRESS_REG0); + grub_pci_write (addr, 0x5026000); + addr = grub_pci_make_address (dev, GRUB_PCI_REG_COMMAND); + grub_pci_write_word (addr, GRUB_PCI_COMMAND_SERR_ENABLE + | GRUB_PCI_COMMAND_PARITY_ERROR + | GRUB_PCI_COMMAND_BUS_MASTER + | GRUB_PCI_COMMAND_MEM_ENABLED); + + addr = grub_pci_make_address (dev, GRUB_PCI_REG_STATUS); + grub_pci_write_word (addr, (1 << GRUB_PCI_STATUS_DEVSEL_TIMING_SHIFT) + | GRUB_PCI_STATUS_CAPABILITIES); + break; + } + return 0; +} + +static void +init_pci (void) +{ + *((volatile grub_uint32_t *) GRUB_CPU_LOONGSON_PCI_HIT1_SEL_LO) = 0x8000000c; + *((volatile grub_uint32_t *) GRUB_CPU_LOONGSON_PCI_HIT1_SEL_HI) = 0xffffffff; + + /* Setup PCI controller. */ + *((volatile grub_uint16_t *) (GRUB_MACHINE_PCI_CONTROLLER_HEADER + + GRUB_PCI_REG_COMMAND)) + = GRUB_PCI_COMMAND_PARITY_ERROR | GRUB_PCI_COMMAND_BUS_MASTER + | GRUB_PCI_COMMAND_MEM_ENABLED; + *((volatile grub_uint16_t *) (GRUB_MACHINE_PCI_CONTROLLER_HEADER + + GRUB_PCI_REG_STATUS)) + = (1 << GRUB_PCI_STATUS_DEVSEL_TIMING_SHIFT) + | GRUB_PCI_STATUS_FAST_B2B_CAPABLE | GRUB_PCI_STATUS_66MHZ_CAPABLE + | GRUB_PCI_STATUS_CAPABILITIES; + + *((volatile grub_uint32_t *) (GRUB_MACHINE_PCI_CONTROLLER_HEADER + + GRUB_PCI_REG_CACHELINE)) = 0xff; + *((volatile grub_uint32_t *) (GRUB_MACHINE_PCI_CONTROLLER_HEADER + + GRUB_PCI_REG_ADDRESS_REG0)) + = 0x80000000 | GRUB_PCI_ADDR_MEM_TYPE_64 | GRUB_PCI_ADDR_MEM_PREFETCH; + *((volatile grub_uint32_t *) (GRUB_MACHINE_PCI_CONTROLLER_HEADER + + GRUB_PCI_REG_ADDRESS_REG1)) = 0; + + grub_pci_iterate (set_card, NULL); +} + +void +grub_machine_init (void) +{ + grub_addr_t modend; + grub_uint32_t prid; + + asm volatile ("mfc0 %0, " GRUB_CPU_LOONGSON_COP0_PRID : "=r" (prid)); + + switch (prid) + { + /* Loongson 2E. */ + case 0x6302: + grub_arch_machine = GRUB_ARCH_MACHINE_FULOONG2E; + grub_bonito_type = GRUB_BONITO_2F; + break; + /* Loongson 2F. */ + case 0x6303: + if (grub_arch_machine != GRUB_ARCH_MACHINE_FULOONG2F + && grub_arch_machine != GRUB_ARCH_MACHINE_YEELOONG) + grub_arch_machine = GRUB_ARCH_MACHINE_YEELOONG; + grub_bonito_type = GRUB_BONITO_2F; + break; + /* Loongson 3A. */ + case 0x6305: + grub_arch_machine = GRUB_ARCH_MACHINE_YEELOONG_3A; + grub_bonito_type = GRUB_BONITO_3A; + break; + } + + /* FIXME: measure this. */ + if (grub_arch_busclock == 0) + { + grub_arch_busclock = 66000000; + grub_arch_cpuclock = 797000000; + } + + grub_install_get_time_ms (grub_rtc_get_time_ms); + + if (grub_arch_memsize == 0) + { + grub_port_t smbbase; + grub_err_t err; + grub_pci_device_t dev; + struct grub_smbus_spd spd; + unsigned totalmem; + int i; + + if (!grub_cs5536_find (&dev)) + grub_fatal ("No CS5536 found\n"); + + err = grub_cs5536_init_smbus (dev, 0x7ff, &smbbase); + if (err) + grub_fatal ("Couldn't init SMBus: %s\n", grub_errmsg); + + /* Yeeloong and Fuloong have only one memory slot. */ + err = grub_cs5536_read_spd (smbbase, GRUB_SMB_RAM_START_ADDR, &spd); + if (err) + grub_fatal ("Couldn't read SPD: %s\n", grub_errmsg); + for (i = 5; i < 13; i++) + if (spd.ddr2.rank_capacity & (1 << (i & 7))) + break; + /* Something is wrong. */ + if (i == 13) + totalmem = 256; + else + totalmem = ((spd.ddr2.num_of_ranks + & GRUB_SMBUS_SPD_MEMORY_NUM_OF_RANKS_MASK) + 1) << (i + 2); + + if (totalmem >= 256) + { + grub_arch_memsize = 256; + grub_arch_highmemsize = totalmem - 256; + } + else + { + grub_arch_memsize = totalmem; + grub_arch_highmemsize = 0; + } + + grub_cs5536_init_geode (dev); + + init_pci (); + } + + modend = grub_modules_get_end (); + grub_mm_init_region ((void *) modend, (grub_arch_memsize << 20) + - (modend - GRUB_ARCH_LOWMEMVSTART)); + /* FIXME: use upper memory as well. */ + + /* Initialize output terminal (can't be done earlier, as gfxterm + relies on a working heap. */ + grub_video_sm712_init (); + grub_video_sis315pro_init (); + grub_video_radeon_fuloong2e_init (); + grub_video_radeon_yeeloong3a_init (); + grub_font_init (); + grub_gfxterm_init (); + + grub_keylayouts_init (); + if (grub_arch_machine == GRUB_ARCH_MACHINE_YEELOONG + || grub_arch_machine == GRUB_ARCH_MACHINE_YEELOONG_3A) + grub_at_keyboard_init (); + + grub_terminfo_init (); + grub_serial_init (); + + grub_boot_init (); +} + +void +grub_machine_fini (int flags __attribute__ ((unused))) +{ +} + +static int +halt_via (grub_pci_device_t dev, grub_pci_id_t pciid, + void *data __attribute__ ((unused))) +{ + grub_uint16_t pm; + grub_pci_address_t addr; + + if (pciid != 0x30571106) + return 0; + + addr = grub_pci_make_address (dev, 0x40); + pm = grub_pci_read (addr) & ~1; + + if (pm == 0) + { + grub_pci_write (addr, 0x1801); + pm = 0x1800; + } + + addr = grub_pci_make_address (dev, 0x80); + grub_pci_write_byte (addr, 0xff); + + addr = grub_pci_make_address (dev, GRUB_PCI_REG_COMMAND); + grub_pci_write_word (addr, grub_pci_read_word (addr) | GRUB_PCI_COMMAND_IO_ENABLED); + + /* FIXME: This one is derived from qemu. Check on real hardware. */ + grub_outw (0x2000, pm + 4 + GRUB_MACHINE_PCI_IO_BASE); + grub_millisleep (5000); + + return 0; +} + +void +grub_halt (void) +{ + switch (grub_arch_machine) + { + case GRUB_ARCH_MACHINE_FULOONG2E: + grub_pci_iterate (halt_via, NULL); + break; + case GRUB_ARCH_MACHINE_FULOONG2F: + { + grub_pci_device_t dev; + grub_port_t p; + if (grub_cs5536_find (&dev)) + { + p = (grub_cs5536_read_msr (dev, GRUB_CS5536_MSR_GPIO_BAR) + & GRUB_CS5536_LBAR_ADDR_MASK) + GRUB_MACHINE_PCI_IO_BASE; + grub_outl ((1 << 13), p + 4); + grub_outl ((1 << 29), p); + grub_millisleep (5000); + } + } + break; + case GRUB_ARCH_MACHINE_YEELOONG: + grub_outb (grub_inb (GRUB_CPU_LOONGSON_GPIOCFG) + & ~GRUB_CPU_YEELOONG_SHUTDOWN_GPIO, GRUB_CPU_LOONGSON_GPIOCFG); + grub_millisleep (1500); + break; + case GRUB_ARCH_MACHINE_YEELOONG_3A: + grub_millisleep (1); + grub_outb (0x4e, GRUB_MACHINE_PCI_IO_BASE_3A | 0x66); + grub_millisleep (1); + grub_outb (2, GRUB_MACHINE_PCI_IO_BASE_3A | 0x62); + grub_millisleep (5000); + break; + } + + grub_puts_ (N_("Shutdown failed")); + grub_refresh (); + while (1); +} + +void +grub_exit (void) +{ + grub_halt (); +} + +void +grub_machine_get_bootlocation (char **device __attribute__ ((unused)), + char **path __attribute__ ((unused))) +{ +} + +extern char _end[]; +grub_addr_t grub_modbase = (grub_addr_t) _end; + diff --git a/grub-core/kern/mips/qemu_mips/init.c b/grub-core/kern/mips/qemu_mips/init.c new file mode 100644 index 0000000..be88b77 --- /dev/null +++ b/grub-core/kern/mips/qemu_mips/init.c @@ -0,0 +1,105 @@ +#include <grub/kernel.h> +#include <grub/misc.h> +#include <grub/env.h> +#include <grub/time.h> +#include <grub/types.h> +#include <grub/misc.h> +#include <grub/mm.h> +#include <grub/time.h> +#include <grub/machine/memory.h> +#include <grub/machine/kernel.h> +#include <grub/machine/console.h> +#include <grub/cpu/memory.h> +#include <grub/memory.h> +#include <grub/video.h> +#include <grub/terminfo.h> +#include <grub/keyboard_layouts.h> +#include <grub/serial.h> +#include <grub/loader.h> +#include <grub/at_keyboard.h> + +static inline int +probe_mem (grub_addr_t addr) +{ + volatile grub_uint8_t *ptr = (grub_uint8_t *) (0xa0000000 | addr); + grub_uint8_t c = *ptr; + *ptr = 0xAA; + if (*ptr != 0xAA) + return 0; + *ptr = 0x55; + if (*ptr != 0x55) + return 0; + *ptr = c; + return 1; +} + +void +grub_machine_init (void) +{ + grub_addr_t modend; + + if (grub_arch_memsize == 0) + { + int i; + + for (i = 27; i >= 0; i--) + if (probe_mem (grub_arch_memsize | (1 << i))) + grub_arch_memsize |= (1 << i); + grub_arch_memsize++; + } + + /* FIXME: measure this. */ + grub_arch_cpuclock = 200000000; + + modend = grub_modules_get_end (); + grub_mm_init_region ((void *) modend, grub_arch_memsize + - (modend - GRUB_ARCH_LOWMEMVSTART)); + + grub_install_get_time_ms (grub_rtc_get_time_ms); + + grub_keylayouts_init (); + grub_at_keyboard_init (); + + grub_qemu_init_cirrus (); + grub_vga_text_init (); + + grub_terminfo_init (); + grub_serial_init (); + + grub_boot_init (); +} + +void +grub_machine_fini (int flags __attribute__ ((unused))) +{ +} + +void +grub_exit (void) +{ + grub_halt (); +} + +void +grub_halt (void) +{ + grub_outl (42, 0xbfbf0004); + while (1); +} + +grub_err_t +grub_machine_mmap_iterate (grub_memory_hook_t hook, void *hook_data) +{ + hook (0, grub_arch_memsize, GRUB_MEMORY_AVAILABLE, hook_data); + return GRUB_ERR_NONE; +} + +void +grub_machine_get_bootlocation (char **device __attribute__ ((unused)), + char **path __attribute__ ((unused))) +{ +} + +extern char _end[]; +grub_addr_t grub_modbase = (grub_addr_t) _end; + diff --git a/grub-core/kern/mips/startup.S b/grub-core/kern/mips/startup.S new file mode 100644 index 0000000..1fdb58a --- /dev/null +++ b/grub-core/kern/mips/startup.S @@ -0,0 +1,126 @@ +/* startup.S - Startup code for the MIPS. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2009 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <grub/symbol.h> +#include <grub/offsets.h> +#include <grub/machine/memory.h> +#include <grub/machine/kernel.h> +#include <grub/offsets.h> +#include <grub/mips/asm.h> + +#define BASE_ADDR 8 + + .globl __start, _start, start + .set noreorder + .set nomacro +__start: +_start: +start: +.extern __bss_start +.extern _end + bal cont + nop + + .org GRUB_KERNEL_MACHINE_TOTAL_MODULE_SIZE +VARIABLE(grub_total_modules_size) + .long 0 + +VARIABLE (grub_arch_busclock) + .long 0 +VARIABLE (grub_arch_cpuclock) + .long 0 +VARIABLE (grub_arch_memsize) + .long 0 +VARIABLE (grub_arch_highmemsize) + .long 0 +#ifdef GRUB_MACHINE_MIPS_LOONGSON +VARIABLE (grub_arch_machine) + .long GRUB_ARCH_MACHINE_FULOONG2F +#endif +cont: + /* Save our base. */ + move $s0, $ra + +#ifdef GRUB_MACHINE_MIPS_QEMU_MIPS + lui $t1, %hi(grub_arch_busclock) + addiu $t1, %lo(grub_arch_busclock) + sw $s4, 8($t1) +#endif + +#ifdef GRUB_MACHINE_MIPS_LOONGSON + lui $t1, %hi(grub_arch_busclock) + addiu $t1, %lo(grub_arch_busclock) + sw $s2, 0($t1) + sw $s3, 4($t1) + sw $s4, 8($t1) + sw $s5, 12($t1) + sw $s7, 16($t1) +#endif + + /* Move the modules out of BSS. */ + lui $t2, %hi(__bss_start) + addiu $t2, %lo(__bss_start) + + lui $t1, %hi(_end) + addiu $t1, %lo(_end) + addiu $t1, (GRUB_KERNEL_MACHINE_MOD_ALIGN - 1) + li $t3, (GRUB_KERNEL_MACHINE_MOD_ALIGN - 1) + nor $t3, $t3, $0 + and $t1, $t1, $t3 + + lw $t3, (GRUB_KERNEL_MACHINE_TOTAL_MODULE_SIZE - BASE_ADDR)($s0) + + /* Backward copy. */ + add $t1, $t1, $t3 + add $t2, $t2, $t3 + addiu $t1, $t1, -1 + addiu $t2, $t2, -1 + + /* $t2 is source. $t1 is destination. $t3 is size. */ +modulesmovcont: + beq $t3, $0, modulesmovdone + nop + lb GRUB_ASM_T4, 0($t2) + sb GRUB_ASM_T4, 0($t1) + addiu $t2, $t2, -1 + addiu $t1, $t1, -1 + b modulesmovcont + addiu $t3, $t3, -1 +modulesmovdone: + + /* Clean BSS. */ + + lui $t1, %hi(__bss_start) + addiu $t1, $t1, %lo(__bss_start) + lui $t2, %hi(_end) + addiu $t2, $t2, %lo(_end) +bsscont: + sb $0,0($t1) + addiu $t1, $t1, 1 + sltu $t3, $t1, $t2 + bne $t3, $0, bsscont + nop + + lui $t9, %hi(grub_main) + addiu $t9, %lo(grub_main) + + lui $sp, %hi(GRUB_MACHINE_MEMORY_STACK_HIGH) + jr $t9 + addiu $sp, $sp, %lo(GRUB_MACHINE_MEMORY_STACK_HIGH) + |