summaryrefslogtreecommitdiffstats
path: root/grub-core/kern/mips
diff options
context:
space:
mode:
Diffstat (limited to 'grub-core/kern/mips')
-rw-r--r--grub-core/kern/mips/arc/init.c463
-rw-r--r--grub-core/kern/mips/cache.S70
-rw-r--r--grub-core/kern/mips/cache_flush.S54
-rw-r--r--grub-core/kern/mips/dl.c274
-rw-r--r--grub-core/kern/mips/init.c38
-rw-r--r--grub-core/kern/mips/loongson/init.c320
-rw-r--r--grub-core/kern/mips/qemu_mips/init.c105
-rw-r--r--grub-core/kern/mips/startup.S126
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)
+