summaryrefslogtreecommitdiffstats
path: root/grub-core/kern/arm
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 16:29:51 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 16:29:51 +0000
commit6e7a315eb67cb6c113cf37e1d66c4f11a51a2b3e (patch)
tree32451fa3cdd9321fb2591fada9891b2cb70a9cd1 /grub-core/kern/arm
parentInitial commit. (diff)
downloadgrub2-upstream.tar.xz
grub2-upstream.zip
Adding upstream version 2.06.upstream/2.06upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'grub-core/kern/arm')
-rw-r--r--grub-core/kern/arm/cache.S123
-rw-r--r--grub-core/kern/arm/cache.c311
-rw-r--r--grub-core/kern/arm/cache_armv6.S72
-rw-r--r--grub-core/kern/arm/cache_armv7.S138
-rw-r--r--grub-core/kern/arm/compiler-rt.S86
-rw-r--r--grub-core/kern/arm/coreboot/cbtable.c40
-rw-r--r--grub-core/kern/arm/coreboot/coreboot.S44
-rw-r--r--grub-core/kern/arm/coreboot/dma.c59
-rw-r--r--grub-core/kern/arm/coreboot/init.c151
-rw-r--r--grub-core/kern/arm/coreboot/timer.c101
-rw-r--r--grub-core/kern/arm/dl.c280
-rw-r--r--grub-core/kern/arm/dl_helper.c245
-rw-r--r--grub-core/kern/arm/efi/init.c77
-rw-r--r--grub-core/kern/arm/efi/startup.S36
-rw-r--r--grub-core/kern/arm/startup.S177
-rw-r--r--grub-core/kern/arm/uboot/init.c70
-rw-r--r--grub-core/kern/arm/uboot/uboot.S73
17 files changed, 2083 insertions, 0 deletions
diff --git a/grub-core/kern/arm/cache.S b/grub-core/kern/arm/cache.S
new file mode 100644
index 0000000..354a069
--- /dev/null
+++ b/grub-core/kern/arm/cache.S
@@ -0,0 +1,123 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2013 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>
+
+ .file "cache.S"
+ .text
+ .syntax unified
+#if !defined (__thumb2__) || !defined (ARMV7)
+ .arm
+#else
+ .thumb
+#endif
+
+#if !defined (ARMV6) && !defined (ARMV7)
+# error Unsupported architecture version!
+#endif
+
+ .align 2
+
+/*
+ * Simple cache maintenance functions
+ */
+
+@ r0 - *beg (inclusive)
+@ r1 - *end (exclusive)
+@void grub_arm_clean_dcache_range (grub_addr_t start, grub_addr_t end, grub_addr_t dlinesz)
+#ifdef ARMV6
+FUNCTION(grub_arm_clean_dcache_range_armv6)
+#else
+FUNCTION(grub_arm_clean_dcache_range_armv7)
+#endif
+ DSB
+ @ Clean data cache for range to point-of-unification
+1: cmp r0, r1
+ bge 2f
+#ifdef ARMV6
+ mcr p15, 0, r0, c7, c10, 1 @ Clean data cache line by MVA
+#else
+ mcr p15, 0, r0, c7, c11, 1 @ DCCMVAU
+#endif
+ add r0, r0, r2 @ Next line
+ b 1b
+2: DSB
+ bx lr
+
+@ r0 - *beg (inclusive)
+@ r1 - *end (exclusive)
+#ifdef ARMV6
+FUNCTION(grub_arm_invalidate_icache_range_armv6)
+#else
+FUNCTION(grub_arm_invalidate_icache_range_armv7)
+#endif
+ @ Invalidate instruction cache for range to point-of-unification
+1: cmp r0, r1
+ bge 2f
+ mcr p15, 0, r0, c7, c5, 1 @ ICIMVAU
+ add r0, r0, r2 @ Next line
+ b 1b
+ @ Branch predictor invalidate all
+2: mcr p15, 0, r0, c7, c5, 6 @ BPIALL
+ DSB
+ ISB
+ bx lr
+
+#ifdef ARMV6
+FUNCTION(grub_arm_disable_caches_mmu_armv6)
+#else
+FUNCTION(grub_arm_disable_caches_mmu_armv7)
+#endif
+
+ push {r4, lr}
+
+ @ disable D-cache
+ mrc p15, 0, r0, c1, c0, 0
+ bic r0, r0, #(1 << 2)
+ mcr p15, 0, r0, c1, c0, 0
+ DSB
+ ISB
+
+ @ clean/invalidate D-cache
+ bl clean_invalidate_dcache
+
+ @ disable I-cache
+ mrc p15, 0, r0, c1, c0, 0
+ bic r0, r0, #(1 << 12)
+ mcr p15, 0, r0, c1, c0, 0
+ DSB
+ ISB
+
+ @ invalidate I-cache (also invalidates branch predictors)
+ mcr p15, 0, r0, c7, c5, 0
+ DSB
+ ISB
+
+ @ clear SCTLR M bit
+ mrc p15, 0, r0, c1, c0, 0
+ bic r0, r0, #(1 << 0)
+ mcr p15, 0, r0, c1, c0, 0
+
+ mcr p15, 0, r0, c8, c7, 0 @ invalidate TLB
+ mcr p15, 0, r0, c7, c5, 6 @ invalidate branch predictor
+ DSB
+ ISB
+
+ pop {r4, lr}
+ bx lr
+
diff --git a/grub-core/kern/arm/cache.c b/grub-core/kern/arm/cache.c
new file mode 100644
index 0000000..6c75193
--- /dev/null
+++ b/grub-core/kern/arm/cache.c
@@ -0,0 +1,311 @@
+#include <grub/dl.h>
+#include <grub/cache.h>
+#include <grub/arm/system.h>
+#ifdef GRUB_MACHINE_UBOOT
+#include <grub/uboot/uboot.h>
+#include <grub/uboot/api_public.h>
+#include <grub/mm.h>
+#endif
+
+/* This is only about cache architecture. It doesn't imply
+ the CPU architecture. */
+static enum
+ {
+ ARCH_UNKNOWN,
+ ARCH_ARMV5_WRITE_THROUGH,
+ ARCH_ARMV6,
+ ARCH_ARMV6_UNIFIED,
+ ARCH_ARMV7
+ } type = ARCH_UNKNOWN;
+
+static int is_v6_mmu;
+
+static grub_uint32_t grub_arch_cache_dlinesz;
+static grub_uint32_t grub_arch_cache_ilinesz;
+static grub_uint32_t grub_arch_cache_max_linesz;
+
+/* Prototypes for asm functions. */
+void grub_arm_clean_dcache_range_armv6 (grub_addr_t start, grub_addr_t end,
+ grub_addr_t dlinesz);
+void grub_arm_clean_dcache_range_armv7 (grub_addr_t start, grub_addr_t end,
+ grub_addr_t dlinesz);
+void grub_arm_clean_dcache_range_poc_armv7 (grub_addr_t start, grub_addr_t end,
+ grub_addr_t dlinesz);
+void grub_arm_invalidate_icache_range_armv6 (grub_addr_t start, grub_addr_t end,
+ grub_addr_t dlinesz);
+void grub_arm_invalidate_icache_range_armv7 (grub_addr_t start, grub_addr_t end,
+ grub_addr_t dlinesz);
+void grub_arm_disable_caches_mmu_armv6 (void);
+void grub_arm_disable_caches_mmu_armv7 (void);
+grub_uint32_t grub_arm_main_id (void);
+grub_uint32_t grub_arm_cache_type (void);
+
+static void
+probe_caches (void)
+{
+ grub_uint32_t main_id, cache_type;
+
+ /* Read main ID Register */
+ main_id = grub_arm_main_id ();
+
+ switch ((main_id >> 16) & 0xf)
+ {
+ case 0x3:
+ case 0x4:
+ case 0x5:
+ case 0x6:
+ is_v6_mmu = 0;
+ break;
+ case 0x7:
+ case 0xf:
+ is_v6_mmu = 1;
+ break;
+ default:
+ grub_fatal ("Unsupported ARM ID 0x%x", main_id);
+ }
+
+ /* Read Cache Type Register */
+ cache_type = grub_arm_cache_type ();
+
+ switch (cache_type >> 24)
+ {
+ case 0x00:
+ case 0x01:
+ grub_arch_cache_dlinesz = 8 << ((cache_type >> 12) & 3);
+ grub_arch_cache_ilinesz = 8 << (cache_type & 3);
+ type = ARCH_ARMV5_WRITE_THROUGH;
+ break;
+ case 0x04:
+ case 0x0a:
+ case 0x0c:
+ case 0x0e:
+ case 0x1c:
+ grub_arch_cache_dlinesz = 8 << ((cache_type >> 12) & 3);
+ grub_arch_cache_ilinesz = 8 << (cache_type & 3);
+ type = ARCH_ARMV6_UNIFIED;
+ break;
+ case 0x05:
+ case 0x0b:
+ case 0x0d:
+ case 0x0f:
+ case 0x1d:
+ grub_arch_cache_dlinesz = 8 << ((cache_type >> 12) & 3);
+ grub_arch_cache_ilinesz = 8 << (cache_type & 3);
+ type = ARCH_ARMV6;
+ break;
+ default:
+ /*
+ * The CTR register is pretty much unchanged from v7 onwards,
+ * and is guaranteed to be backward compatible (the IDC/DIC bits
+ * allow certain CMOs to be elided, but performing them is never
+ * wrong), hence handling it like its AArch64 equivalent.
+ */
+ grub_arch_cache_dlinesz = 4 << ((cache_type >> 16) & 0xf);
+ grub_arch_cache_ilinesz = 4 << (cache_type & 0xf);
+ type = ARCH_ARMV7;
+ }
+ if (grub_arch_cache_dlinesz > grub_arch_cache_ilinesz)
+ grub_arch_cache_max_linesz = grub_arch_cache_dlinesz;
+ else
+ grub_arch_cache_max_linesz = grub_arch_cache_ilinesz;
+}
+
+#ifdef GRUB_MACHINE_UBOOT
+
+static void subdivide (grub_uint32_t *table, grub_uint32_t *subtable,
+ grub_uint32_t addr)
+{
+ grub_uint32_t j;
+ addr = addr >> 20 << 20;
+ table[addr >> 20] = (grub_addr_t) subtable | 1;
+ for (j = 0; j < 256; j++)
+ subtable[j] = addr | (j << 12)
+ | (3 << 4) | (3 << 6) | (3 << 8) | (3 << 10)
+ | (0 << 3) | (1 << 2) | 2;
+}
+
+void
+grub_arm_enable_caches_mmu (void)
+{
+ grub_uint32_t *table;
+ grub_uint32_t i;
+ grub_uint32_t border_crossing = 0;
+ grub_uint32_t *subtable;
+ struct sys_info *si = grub_uboot_get_sys_info ();
+
+ if (!si || (si->mr_no == 0))
+ {
+ grub_printf ("couldn't get memory map, not enabling caches");
+ grub_errno = GRUB_ERR_NONE;
+ return;
+ }
+
+ if (type == ARCH_UNKNOWN)
+ probe_caches ();
+
+ for (i = 0; (signed) i < si->mr_no; i++)
+ {
+ if (si->mr[i].start & ((1 << 20) - 1))
+ border_crossing++;
+ if ((si->mr[i].start + si->mr[i].size) & ((1 << 20) - 1))
+ border_crossing++;
+ }
+
+ grub_printf ("%d crossers\n", border_crossing);
+
+ table = grub_memalign (1 << 14, (1 << 14) + (border_crossing << 10));
+ if (!table)
+ {
+ grub_printf ("couldn't allocate place for MMU table, not enabling caches");
+ grub_errno = GRUB_ERR_NONE;
+ return;
+ }
+
+ subtable = table + (1 << 12);
+ /* Map all unknown as device. */
+ for (i = 0; i < (1 << 12); i++)
+ table[i] = (i << 20) | (3 << 10) | (0 << 3) | (1 << 2) | 2;
+ /*
+ Device: TEX= 0, C=0, B=1
+ normal: TEX= 0, C=1, B=1
+ AP = 3
+ IMP = 0
+ Domain = 0
+*/
+
+ for (i = 0; (signed) i < si->mr_no; i++)
+ {
+ if (si->mr[i].start & ((1 << 20) - 1))
+ {
+ subdivide (table, subtable, si->mr[i].start);
+ subtable += (1 << 8);
+ }
+ if ((si->mr[i].start + si->mr[i].size) & ((1 << 20) - 1))
+ {
+ subdivide (table, subtable, si->mr[i].start + si->mr[i].size);
+ subtable += (1 << 8);
+ }
+ }
+
+ for (i = 0; (signed) i < si->mr_no; i++)
+ if ((si->mr[i].flags & MR_ATTR_MASK) == MR_ATTR_DRAM
+ || (si->mr[i].flags & MR_ATTR_MASK) == MR_ATTR_SRAM
+ || (si->mr[i].flags & MR_ATTR_MASK) == MR_ATTR_FLASH)
+ {
+ grub_uint32_t cur, end;
+ cur = si->mr[i].start;
+ end = si->mr[i].start + si->mr[i].size;
+ while (cur < end)
+ {
+ grub_uint32_t *st;
+ if ((table[cur >> 20] & 3) == 2)
+ {
+ cur = cur >> 20 << 20;
+ table[cur >> 20] = cur | (3 << 10) | (1 << 3) | (1 << 2) | 2;
+ cur += (1 << 20);
+ continue;
+ }
+ cur = cur >> 12 << 12;
+ st = (grub_uint32_t *) (table[cur >> 20] & ~0x3ff);
+ st[(cur >> 12) & 0xff] = cur | (3 << 4) | (3 << 6)
+ | (3 << 8) | (3 << 10)
+ | (1 << 3) | (1 << 2) | 2;
+ cur += (1 << 12);
+ }
+ }
+
+ grub_printf ("MMU tables generated\n");
+ if (is_v6_mmu)
+ grub_arm_clear_mmu_v6 ();
+
+ grub_printf ("enabling MMU\n");
+ grub_arm_enable_mmu (table);
+ grub_printf ("MMU enabled\n");
+}
+
+#endif
+
+void
+grub_arch_sync_caches (void *address, grub_size_t len)
+{
+ grub_addr_t start = (grub_addr_t) address;
+ grub_addr_t end = start + len;
+
+ if (type == ARCH_UNKNOWN)
+ probe_caches ();
+ start = ALIGN_DOWN (start, grub_arch_cache_max_linesz);
+ end = ALIGN_UP (end, grub_arch_cache_max_linesz);
+ switch (type)
+ {
+ case ARCH_ARMV6:
+ grub_arm_clean_dcache_range_armv6 (start, end, grub_arch_cache_dlinesz);
+ grub_arm_invalidate_icache_range_armv6 (start, end,
+ grub_arch_cache_ilinesz);
+ break;
+ case ARCH_ARMV7:
+ grub_arm_clean_dcache_range_armv7 (start, end, grub_arch_cache_dlinesz);
+ grub_arm_invalidate_icache_range_armv7 (start, end,
+ grub_arch_cache_ilinesz);
+ break;
+ /* Nothing to do. */
+ case ARCH_ARMV5_WRITE_THROUGH:
+ case ARCH_ARMV6_UNIFIED:
+ break;
+ /* Pacify GCC. */
+ case ARCH_UNKNOWN:
+ break;
+ }
+}
+
+void
+grub_arch_sync_dma_caches (volatile void *address, grub_size_t len)
+{
+ grub_addr_t start = (grub_addr_t) address;
+ grub_addr_t end = start + len;
+
+ if (type == ARCH_UNKNOWN)
+ probe_caches ();
+ start = ALIGN_DOWN (start, grub_arch_cache_max_linesz);
+ end = ALIGN_UP (end, grub_arch_cache_max_linesz);
+ switch (type)
+ {
+ case ARCH_ARMV6:
+ grub_arm_clean_dcache_range_armv6 (start, end, grub_arch_cache_dlinesz);
+ grub_arm_invalidate_icache_range_armv6 (start, end,
+ grub_arch_cache_ilinesz);
+ break;
+ case ARCH_ARMV5_WRITE_THROUGH:
+ case ARCH_ARMV6_UNIFIED:
+ grub_arm_clean_dcache_range_armv6 (start, end, grub_arch_cache_dlinesz);
+ break;
+ case ARCH_ARMV7:
+ grub_arm_clean_dcache_range_poc_armv7 (start, end, grub_arch_cache_dlinesz);
+ grub_arm_invalidate_icache_range_armv7 (start, end,
+ grub_arch_cache_ilinesz);
+ break;
+ /* Pacify GCC. */
+ case ARCH_UNKNOWN:
+ break;
+ }
+}
+
+void
+grub_arm_disable_caches_mmu (void)
+{
+ if (type == ARCH_UNKNOWN)
+ probe_caches ();
+ switch (type)
+ {
+ case ARCH_ARMV5_WRITE_THROUGH:
+ case ARCH_ARMV6_UNIFIED:
+ case ARCH_ARMV6:
+ grub_arm_disable_caches_mmu_armv6 ();
+ break;
+ case ARCH_ARMV7:
+ grub_arm_disable_caches_mmu_armv7 ();
+ break;
+ /* Pacify GCC. */
+ case ARCH_UNKNOWN:
+ break;
+ }
+}
diff --git a/grub-core/kern/arm/cache_armv6.S b/grub-core/kern/arm/cache_armv6.S
new file mode 100644
index 0000000..dfaded0
--- /dev/null
+++ b/grub-core/kern/arm/cache_armv6.S
@@ -0,0 +1,72 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2013 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>
+
+ .file "cache_armv6.S"
+ .text
+ .syntax unified
+ .arm
+
+# define DMB mcr p15, 0, r0, c7, c10, 5
+# define DSB mcr p15, 0, r0, c7, c10, 4
+# define ISB mcr p15, 0, r0, c7, c5, 4
+#define ARMV6 1
+
+clean_invalidate_dcache:
+ mcr p15, 0, r0, c7, c14, 0 @ Clean/Invalidate D-cache
+ bx lr
+
+#include "cache.S"
+
+FUNCTION(grub_arm_main_id)
+ mrc p15, 0, r0, c0, c0, 0
+ bx lr
+
+FUNCTION(grub_arm_cache_type)
+ mrc p15, 0, r0, c0, c0, 1
+ bx lr
+
+FUNCTION(grub_arm_clear_mmu_v6)
+ mov r0, #0
+ mcr p15, 0, r0, c2, c0, 2
+ bx lr
+
+FUNCTION(grub_arm_enable_mmu)
+ mcr p15, 0, r0, c2, c0, 0
+
+ mvn r0, #0
+ mcr p15, 0, r0, c3, c0, 0
+
+ mrc p15, 0, r0, c1, c0, 0
+ bic r0, r0, #(1 << 23)
+ mcr p15, 0, r0, c1, c0, 0
+
+ mrc p15, 0, r0, c1, c0, 0
+ orr r0, r0, #(1 << 0)
+ mcr p15, 0, r0, c1, c0, 0
+
+ mrc p15, 0, r0, c1, c0, 0
+ orr r0, r0, #(1 << 2)
+ mcr p15, 0, r0, c1, c0, 0
+
+ mrc p15, 0, r0, c1, c0, 0
+ orr r0, r0, #(1 << 12)
+ mcr p15, 0, r0, c1, c0, 0
+
+ bx lr
diff --git a/grub-core/kern/arm/cache_armv7.S b/grub-core/kern/arm/cache_armv7.S
new file mode 100644
index 0000000..5ae76a3
--- /dev/null
+++ b/grub-core/kern/arm/cache_armv7.S
@@ -0,0 +1,138 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2013 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>
+
+ .file "cache_armv7.S"
+ .text
+ .syntax unified
+#if !defined (__thumb2__)
+ .arch armv7a
+ .arm
+#else
+ .arch armv7
+ .thumb
+#endif
+# define DMB dmb
+# define DSB dsb
+# define ISB isb
+#define ARMV7 1
+
+FUNCTION(grub_arm_clean_dcache_range_poc_armv7)
+ DSB
+ @ Clean data cache for range to point-of-coherence
+1: cmp r0, r1
+ bge 2f
+ mcr p15, 0, r0, c7, c14, 1 @ DCCMVAC
+ add r0, r0, r2 @ Next line
+ b 1b
+2: DSB
+ bx lr
+
+
+ @ r0 - CLIDR
+ @ r1 - LoC
+ @ r2 - current level
+ @ r3 - num sets
+ @ r4 - num ways
+ @ r5 - current set
+ @ r6 - current way
+ @ r7 - line size
+ @ r8 - scratch
+ @ r9 - scratch
+ @ r10 - scratch
+ @ r11 - scratch
+clean_invalidate_dcache:
+ push {r4-r12, lr}
+ mrc p15, 1, r0, c0, c0, 1 @ Read CLIDR
+ lsr r1, r0, #24 @ Extract LoC
+ and r1, r1, #0x7
+
+ mov r2, #0 @ First level, L1
+2: and r8, r0, #7 @ cache type at current level
+ cmp r8, #2
+ blt 5f @ instruction only, or none, skip level
+
+ @ set current cache level/type (for CCSIDR read)
+ lsl r8, r2, #1
+ mcr p15, 2, r8, c0, c0, 0 @ Write CSSELR (level, type: data/uni)
+
+ @ read current cache information
+ mrc p15, 1, r8, c0, c0, 0 @ Read CCSIDR
+ lsr r3, r8, #13 @ Number of sets -1
+
+ @ Keep only 14 bits of r3
+ lsl r3, r3, #18
+ lsr r3, r3, #18
+
+ lsr r4, r8, #3 @ Number of ways -1
+
+ @ Keep only 9 bits of r4
+ lsl r4, r4, #23
+ lsr r4, r4, #23
+
+ and r7, r8, #7 @ log2(line size in words) - 2
+ add r7, r7, #2 @ adjust
+ mov r8, #1
+ lsl r7, r8, r7 @ -> line size in words
+ lsl r7, r7, #2 @ -> bytes
+
+ @ set loop
+ mov r5, #0 @ current set = 0
+3: lsl r8, r2, #1 @ insert level
+ clz r9, r7 @ calculate set field offset
+ mov r10, #31
+ sub r9, r10, r9
+ lsl r10, r5, r9
+ orr r8, r8, r10 @ insert set field
+
+ @ way loop
+ @ calculate way field offset
+ mov r6, #0 @ current way = 0
+ add r10, r4, #1
+ clz r9, r10 @ r9 = way field offset
+ add r9, r9, #1
+4: lsl r10, r6, r9
+ orr r11, r8, r10 @ insert way field
+
+ @ clean and invalidate line by set/way
+ mcr p15, 0, r11, c7, c14, 2 @ DCCISW
+
+ @ next way
+ add r6, r6, #1
+ cmp r6, r4
+ ble 4b
+
+ @ next set
+ add r5, r5, #1
+ cmp r5, r3
+ ble 3b
+
+ @ next level
+5: lsr r0, r0, #3 @ align next level CLIDR 'type' field
+ add r2, r2, #1 @ increment cache level counter
+ cmp r2, r1
+ blt 2b @ outer loop
+
+ @ return
+6: DSB
+ ISB
+ pop {r4-r12, lr}
+ bx lr
+
+#include "cache.S" \ No newline at end of file
diff --git a/grub-core/kern/arm/compiler-rt.S b/grub-core/kern/arm/compiler-rt.S
new file mode 100644
index 0000000..645b42f
--- /dev/null
+++ b/grub-core/kern/arm/compiler-rt.S
@@ -0,0 +1,86 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2013 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/dl.h>
+
+ .file "misc.S"
+ .text
+ .syntax unified
+#if !defined (__thumb2__)
+ .arm
+#else
+ .thumb
+#endif
+
+ .align 2
+
+FUNCTION(__muldi3)
+FUNCTION(__aeabi_lmul)
+ stmfd sp!, {r4, fp}
+ add fp, sp, #4
+ sub sp, sp, #16
+ str r0, [fp, #-12]
+ str r1, [fp, #-8]
+ str r2, [fp, #-20]
+ str r3, [fp, #-16]
+ ldr r3, [fp, #-8]
+ ldr r2, [fp, #-20]
+ mul r2, r3, r2
+ ldr r3, [fp, #-16]
+ ldr r1, [fp, #-12]
+ mul r3, r1, r3
+ add r2, r2, r3
+ ldr r0, [fp, #-12]
+ ldr r1, [fp, #-20]
+ umull r3, r4, r0, r1
+ add r2, r2, r4
+ mov r4, r2
+ mov r0, r3
+ mov r1, r4
+ mov sp, fp
+ sub sp, sp, #4
+ ldmfd sp!, {r4, fp}
+ bx lr
+
+ .macro division32 parent
+
+ sub sp, sp, #8 @ Allocate naturally aligned 64-bit space
+ stmfd sp!, {r3,lr} @ Dummy r3 to maintain stack alignment
+ add r2, sp, #8 @ Set r2 to address of 64-bit space
+ bl \parent
+ ldr r1, [sp, #8] @ Extract remainder
+ ldmfd sp!, {r3,lr} @ Pop into an unused arg/scratch register
+ add sp, sp, #8
+ bx lr
+ .endm
+
+FUNCTION(__aeabi_uidivmod)
+ division32 grub_divmod32
+FUNCTION(__aeabi_idivmod)
+ division32 grub_divmod32s
+
+/*
+ * Null divide-by-zero handler
+ */
+FUNCTION(__aeabi_unwind_cpp_pr0)
+FUNCTION(raise)
+ mov r0, #0
+ bx lr
+
+ END
diff --git a/grub-core/kern/arm/coreboot/cbtable.c b/grub-core/kern/arm/coreboot/cbtable.c
new file mode 100644
index 0000000..8a655bb
--- /dev/null
+++ b/grub-core/kern/arm/coreboot/cbtable.c
@@ -0,0 +1,40 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2007,2008,2013 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/coreboot/lbio.h>
+#include <grub/types.h>
+#include <grub/err.h>
+#include <grub/misc.h>
+#include <grub/dl.h>
+#include <grub/arm/startup.h>
+
+GRUB_MOD_LICENSE ("GPLv3+");
+
+#pragma GCC diagnostic ignored "-Wcast-align"
+
+grub_linuxbios_table_header_t
+grub_linuxbios_get_tables (void)
+{
+ grub_linuxbios_table_header_t table_header
+ = (grub_linuxbios_table_header_t) grub_arm_saved_registers.r[0];
+
+ if (!grub_linuxbios_check_signature (table_header))
+ return 0;
+
+ return table_header;
+}
diff --git a/grub-core/kern/arm/coreboot/coreboot.S b/grub-core/kern/arm/coreboot/coreboot.S
new file mode 100644
index 0000000..a110452
--- /dev/null
+++ b/grub-core/kern/arm/coreboot/coreboot.S
@@ -0,0 +1,44 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2016 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>
+
+ .file "coreboot.S"
+ .text
+ .syntax unified
+#if !defined (__thumb2__)
+ .arch armv7a
+ .arm
+#else
+ .arch armv7
+ .thumb
+#endif
+
+FUNCTION(grub_arm_pfr1)
+ mrc p15, 0, r0, c0, c1, 1
+ bx lr
+
+FUNCTION(grub_armv7_get_timer_value)
+ isb
+ mrrc p15, 1, r0, r1, c14
+ bx lr
+
+FUNCTION(grub_armv7_get_timer_frequency)
+ mrc p15, 0, r0, c14, c0, 0
+ bx lr
+
diff --git a/grub-core/kern/arm/coreboot/dma.c b/grub-core/kern/arm/coreboot/dma.c
new file mode 100644
index 0000000..2c2a627
--- /dev/null
+++ b/grub-core/kern/arm/coreboot/dma.c
@@ -0,0 +1,59 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 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/dma.h>
+#include <grub/mm.h>
+#include <grub/misc.h>
+#include <grub/mm_private.h>
+#include <grub/cache.h>
+
+struct grub_pci_dma_chunk *
+grub_memalign_dma32 (grub_size_t align, grub_size_t size)
+{
+ void *ret;
+ if (align < 64)
+ align = 64;
+ size = ALIGN_UP (size, align);
+ ret = grub_memalign (align, size);
+ if (!ret)
+ return 0;
+ grub_arch_sync_dma_caches (ret, size);
+ return ret;
+}
+
+void
+grub_dma_free (struct grub_pci_dma_chunk *ch)
+{
+ grub_size_t size = (((struct grub_mm_header *) ch) - 1)->size * GRUB_MM_ALIGN;
+ grub_arch_sync_dma_caches (ch, size);
+ grub_free (ch);
+}
+
+volatile void *
+grub_dma_get_virt (struct grub_pci_dma_chunk *ch)
+{
+ return (void *) ch;
+}
+
+grub_uint32_t
+grub_dma_get_phys (struct grub_pci_dma_chunk *ch)
+{
+ return (grub_uint32_t) (grub_addr_t) ch;
+}
+
diff --git a/grub-core/kern/arm/coreboot/init.c b/grub-core/kern/arm/coreboot/init.c
new file mode 100644
index 0000000..8d8c5b8
--- /dev/null
+++ b/grub-core/kern/arm/coreboot/init.c
@@ -0,0 +1,151 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2002,2003,2004,2005,2006,2007,2008,2009,2013 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/mm.h>
+#include <grub/memory.h>
+#include <grub/machine/console.h>
+#include <grub/machine/kernel.h>
+#include <grub/offsets.h>
+#include <grub/types.h>
+#include <grub/err.h>
+#include <grub/dl.h>
+#include <grub/misc.h>
+#include <grub/loader.h>
+#include <grub/env.h>
+#include <grub/cache.h>
+#include <grub/time.h>
+#include <grub/symbol.h>
+#include <grub/video.h>
+#include <grub/coreboot/lbio.h>
+#include <grub/fdtbus.h>
+
+extern grub_uint8_t _start[];
+extern grub_uint8_t _end[];
+extern grub_uint8_t _edata[];
+grub_addr_t start_of_ram = ~(grub_addr_t)0;
+
+void __attribute__ ((noreturn))
+grub_exit (void)
+{
+ /* We can't use grub_fatal() in this function. This would create an infinite
+ loop, since grub_fatal() calls grub_abort() which in turn calls grub_exit(). */
+ while (1)
+ grub_cpu_idle ();
+}
+
+static grub_uint64_t modend;
+static int have_memory = 0;
+
+/* Helper for grub_machine_init. */
+static int
+heap_init (grub_uint64_t addr, grub_uint64_t size, grub_memory_type_t type,
+ void *data __attribute__ ((unused)))
+{
+ grub_uint64_t begin = addr, end = addr + size;
+
+#if GRUB_CPU_SIZEOF_VOID_P == 4
+ /* Restrict ourselves to 32-bit memory space. */
+ if (begin > GRUB_ULONG_MAX)
+ return 0;
+ if (end > GRUB_ULONG_MAX)
+ end = GRUB_ULONG_MAX;
+#endif
+
+ if (start_of_ram > begin)
+ start_of_ram = begin;
+
+ if (type != GRUB_MEMORY_AVAILABLE)
+ return 0;
+
+ if (modend && begin < modend)
+ {
+ if (begin < (grub_addr_t)_start)
+ {
+ grub_mm_init_region ((void *) (grub_addr_t) begin, (grub_size_t) ((grub_addr_t)_start - begin));
+ have_memory = 1;
+ }
+ begin = modend;
+ }
+
+ /* Avoid DMA problems. */
+ if (end >= 0xfe000000)
+ end = 0xfe000000;
+
+ if (end <= begin)
+ return 0;
+
+ grub_mm_init_region ((void *) (grub_addr_t) begin, (grub_size_t) (end - begin));
+
+ have_memory = 1;
+
+ return 0;
+}
+
+void
+grub_machine_init (void)
+{
+ struct grub_module_header *header;
+ void *dtb = 0;
+ grub_size_t dtb_size = 0;
+
+ modend = grub_modules_get_end ();
+
+ grub_video_coreboot_fb_early_init ();
+
+ grub_machine_mmap_iterate (heap_init, NULL);
+ if (!have_memory)
+ grub_fatal ("No memory found");
+
+ grub_video_coreboot_fb_late_init ();
+
+ grub_font_init ();
+ grub_gfxterm_init ();
+
+ FOR_MODULES (header)
+ if (header->type == OBJ_TYPE_DTB)
+ {
+ char *dtb_orig_addr, *dtb_copy;
+ dtb_orig_addr = (char *) header + sizeof (struct grub_module_header);
+
+ dtb_size = header->size - sizeof (struct grub_module_header);
+ dtb = dtb_copy = grub_malloc (dtb_size);
+ grub_memmove (dtb_copy, dtb_orig_addr, dtb_size);
+ break;
+ }
+ if (!dtb)
+ grub_fatal ("No DTB found");
+ grub_fdtbus_init (dtb, dtb_size);
+
+ grub_rk3288_spi_init ();
+
+ grub_machine_timer_init ();
+ grub_cros_init ();
+ grub_pl050_init ();
+}
+
+void
+grub_machine_get_bootlocation (char **device __attribute__ ((unused)),
+ char **path __attribute__ ((unused)))
+{
+}
+
+void
+grub_machine_fini (int flags __attribute__ ((unused)))
+{
+}
diff --git a/grub-core/kern/arm/coreboot/timer.c b/grub-core/kern/arm/coreboot/timer.c
new file mode 100644
index 0000000..d97b844
--- /dev/null
+++ b/grub-core/kern/arm/coreboot/timer.c
@@ -0,0 +1,101 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2016 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/mm.h>
+#include <grub/machine/kernel.h>
+#include <grub/types.h>
+#include <grub/err.h>
+#include <grub/time.h>
+#include <grub/fdtbus.h>
+#include <grub/misc.h>
+
+grub_uint64_t
+grub_armv7_get_timer_value(void);
+
+grub_uint32_t
+grub_armv7_get_timer_frequency(void);
+
+grub_uint32_t
+grub_arm_pfr1(void);
+
+static int have_timer = 0;
+static volatile grub_uint32_t *sp804_regs;
+
+static grub_uint64_t
+sp804_get_time_ms (void)
+{
+ static grub_uint32_t high, last_low;
+ grub_uint32_t low = ~sp804_regs[1];
+ if (last_low > low)
+ high++;
+ last_low = low;
+ return grub_divmod64 ((((grub_uint64_t) high) << 32) | low,
+ 1000, 0);
+}
+
+static grub_err_t
+sp804_attach(const struct grub_fdtbus_dev *dev)
+{
+ if (have_timer)
+ return GRUB_ERR_NONE;
+ sp804_regs = grub_fdtbus_map_reg (dev, 0, 0);
+ if (!grub_fdtbus_is_mapping_valid (sp804_regs))
+ return grub_error (GRUB_ERR_IO, "could not map sp804: %p", sp804_regs);
+ grub_install_get_time_ms (sp804_get_time_ms);
+ have_timer = 1;
+ return GRUB_ERR_NONE;
+}
+
+struct grub_fdtbus_driver sp804 =
+{
+ .compatible = "arm,sp804",
+ .attach = sp804_attach
+};
+
+static grub_uint32_t timer_frequency_in_khz;
+
+static grub_uint64_t
+generic_get_time_ms (void)
+{
+ return grub_divmod64 (grub_armv7_get_timer_value(), timer_frequency_in_khz, 0);
+}
+
+static int
+try_generic_timer (void)
+{
+ if (((grub_arm_pfr1 () >> 16) & 0xf) != 1)
+ return 0;
+ grub_printf ("freq = %x\n", grub_armv7_get_timer_frequency());
+ timer_frequency_in_khz = 0x016e3600 / 1000; //grub_armv7_get_timer_frequency() / 1000;
+ if (timer_frequency_in_khz == 0)
+ return 0;
+ grub_install_get_time_ms (generic_get_time_ms);
+ have_timer = 1;
+ return 1;
+}
+
+void
+grub_machine_timer_init (void)
+{
+ grub_fdtbus_register (&sp804);
+
+ if (!have_timer)
+ try_generic_timer ();
+ if (!have_timer)
+ grub_fatal ("No timer found");
+}
diff --git a/grub-core/kern/arm/dl.c b/grub-core/kern/arm/dl.c
new file mode 100644
index 0000000..eab9d17
--- /dev/null
+++ b/grub-core/kern/arm/dl.c
@@ -0,0 +1,280 @@
+/* dl.c - arch-dependent part of loadable module support */
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2013 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/mm.h>
+#include <grub/i18n.h>
+#include <grub/arm/reloc.h>
+
+struct trampoline_arm
+{
+#define ARM_LOAD_IP 0xe59fc000
+#define ARM_BX 0xe12fff1c
+#define ARM_MOV_PC 0xe1a0f00c
+ grub_uint32_t load_ip; /* ldr ip, [pc] */
+ grub_uint32_t bx; /* bx ip or mov pc, ip*/
+ grub_uint32_t addr;
+};
+
+static grub_uint16_t thumb_template[8] =
+ {
+ 0x468c, /* mov ip, r1 */
+ 0x4903, /* ldr r1, [pc, #12] ; (10 <.text+0x10>) */
+ /* Exchange R1 and IP in limited Thumb instruction set.
+ IP gets negated but we compensate it by C code. */
+ /* R1 IP */
+ /* -A R1 */
+ 0x4461, /* add r1, ip */ /* R1-A R1 */
+ 0x4249, /* negs r1, r1 */ /* A-R1 R1 */
+ 0x448c, /* add ip, r1 */ /* A-R1 A */
+ 0x4249, /* negs r1, r1 */ /* R1-A A */
+ 0x4461, /* add r1, ip */ /* R1 A */
+ 0x4760 /* bx ip */
+ };
+
+struct trampoline_thumb
+{
+ grub_uint16_t template[8];
+ grub_uint32_t neg_addr;
+};
+
+#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;
+ unsigned i;
+
+ *tramp = 0;
+ *got = 0;
+
+ for (i = 0, s = (const Elf_Shdr *) ((grub_addr_t) e + e->e_shoff);
+ i < e->e_shnum;
+ i++, s = (const Elf_Shdr *) ((grub_addr_t) s + e->e_shentsize))
+ if (s->sh_type == SHT_REL)
+ {
+ const Elf_Rel *rel, *max;
+
+ for (rel = (const Elf_Rel *) ((grub_addr_t) e + s->sh_offset),
+ max = (const Elf_Rel *) ((grub_addr_t) rel + s->sh_size);
+ rel + 1 <= max;
+ rel = (const Elf_Rel *) ((grub_addr_t) rel + s->sh_entsize))
+ switch (ELF_R_TYPE (rel->r_info))
+ {
+ case R_ARM_CALL:
+ case R_ARM_JUMP24:
+ {
+ *tramp += sizeof (struct trampoline_arm);
+ break;
+ }
+ case R_ARM_THM_CALL:
+ case R_ARM_THM_JUMP24:
+ case R_ARM_THM_JUMP19:
+ {
+ *tramp += sizeof (struct trampoline_thumb);
+ break;
+ }
+ }
+ }
+
+ grub_dprintf ("dl", "trampoline size %x\n", *tramp);
+
+ return GRUB_ERR_NONE;
+}
+
+/*************************************************
+ * Runtime dynamic linker with helper functions. *
+ *************************************************/
+grub_err_t
+grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr,
+ Elf_Shdr *s, grub_dl_segment_t seg)
+{
+ Elf_Rel *rel, *max;
+
+ for (rel = (Elf_Rel *) ((char *) ehdr + s->sh_offset),
+ max = (Elf_Rel *) ((char *) rel + s->sh_size);
+ rel < max;
+ rel = (Elf_Rel *) ((char *) rel + s->sh_entsize))
+ {
+ Elf_Addr *target, sym_addr;
+ grub_err_t retval;
+ Elf_Sym *sym;
+
+ if (seg->size < rel->r_offset)
+ return grub_error (GRUB_ERR_BAD_MODULE,
+ "reloc offset is out of the segment");
+ target = (void *) ((char *) seg->addr + rel->r_offset);
+ sym = (Elf_Sym *) ((char *) mod->symtab
+ + mod->symsize * ELF_R_SYM (rel->r_info));
+
+ sym_addr = sym->st_value;
+
+ switch (ELF_R_TYPE (rel->r_info))
+ {
+ case R_ARM_ABS32:
+ {
+ /* Data will be naturally aligned */
+ retval = grub_arm_reloc_abs32 (target, sym_addr);
+ if (retval != GRUB_ERR_NONE)
+ return retval;
+ }
+ break;
+ case R_ARM_CALL:
+ case R_ARM_JUMP24:
+ {
+ grub_int32_t offset;
+
+ sym_addr += grub_arm_jump24_get_offset (target);
+ offset = sym_addr - (grub_uint32_t) target;
+
+ if ((sym_addr & 1) || !grub_arm_jump24_check_offset (offset))
+ {
+ struct trampoline_arm *tp = mod->trampptr;
+ mod->trampptr = tp + 1;
+ tp->load_ip = ARM_LOAD_IP;
+ tp->bx = (sym_addr & 1) ? ARM_BX : ARM_MOV_PC;
+ tp->addr = sym_addr + 8;
+ offset = (grub_uint8_t *) tp - (grub_uint8_t *) target - 8;
+ }
+ if (!grub_arm_jump24_check_offset (offset))
+ return grub_error (GRUB_ERR_BAD_MODULE,
+ "trampoline out of range");
+ grub_arm_jump24_set_offset (target, offset);
+ }
+ break;
+ case R_ARM_THM_CALL:
+ case R_ARM_THM_JUMP24:
+ {
+ /* Thumb instructions can be 16-bit aligned */
+ grub_int32_t offset;
+
+ sym_addr += grub_arm_thm_call_get_offset ((grub_uint16_t *) target);
+
+ grub_dprintf ("dl", " sym_addr = 0x%08x\n", sym_addr);
+ if (ELF_ST_TYPE (sym->st_info) != STT_FUNC)
+ sym_addr |= 1;
+
+ offset = sym_addr - (grub_uint32_t) target;
+
+ grub_dprintf("dl", " BL*: target=%p, sym_addr=0x%08x, offset=%d\n",
+ target, sym_addr, offset);
+
+ if (!(sym_addr & 1) || (offset < -0x200000 || offset >= 0x200000))
+ {
+ struct trampoline_thumb *tp = mod->trampptr;
+ mod->trampptr = tp + 1;
+ grub_memcpy (tp->template, thumb_template, sizeof (tp->template));
+ tp->neg_addr = -sym_addr - 4;
+ offset = ((grub_uint8_t *) tp - (grub_uint8_t *) target - 4) | 1;
+ }
+
+ if (offset < -0x200000 || offset >= 0x200000)
+ return grub_error (GRUB_ERR_BAD_MODULE,
+ "trampoline out of range");
+
+ grub_dprintf ("dl", " relative destination = %p\n",
+ (char *) target + offset);
+
+ retval = grub_arm_thm_call_set_offset ((grub_uint16_t *) target, offset);
+ if (retval != GRUB_ERR_NONE)
+ return retval;
+ }
+ break;
+ /* Happens when compiled with -march=armv4. Since currently we need
+ at least armv5, keep bx as-is.
+ */
+ case R_ARM_V4BX:
+ break;
+ case R_ARM_THM_MOVW_ABS_NC:
+ case R_ARM_THM_MOVT_ABS:
+ {
+ grub_uint32_t offset;
+ offset = grub_arm_thm_movw_movt_get_value((grub_uint16_t *) target);
+ offset += sym_addr;
+
+ if (ELF_R_TYPE (rel->r_info) == R_ARM_THM_MOVT_ABS)
+ offset >>= 16;
+ else
+ offset &= 0xffff;
+
+ grub_arm_thm_movw_movt_set_value((grub_uint16_t *) target, offset);
+ }
+ break;
+ case R_ARM_THM_JUMP19:
+ {
+ /* Thumb instructions can be 16-bit aligned */
+ grub_int32_t offset;
+
+ sym_addr += grub_arm_thm_jump19_get_offset ((grub_uint16_t *) target);
+
+ if (ELF_ST_TYPE (sym->st_info) != STT_FUNC)
+ sym_addr |= 1;
+
+ offset = sym_addr - (grub_uint32_t) target;
+
+ if (!grub_arm_thm_jump19_check_offset (offset)
+ || !(sym_addr & 1))
+ {
+ struct trampoline_thumb *tp = mod->trampptr;
+ mod->trampptr = tp + 1;
+ grub_memcpy (tp->template, thumb_template, sizeof (tp->template));
+ tp->neg_addr = -sym_addr - 4;
+ offset = ((grub_uint8_t *) tp - (grub_uint8_t *) target - 4) | 1;
+ }
+
+ if (!grub_arm_thm_jump19_check_offset (offset))
+ return grub_error (GRUB_ERR_BAD_MODULE,
+ "trampoline out of range");
+
+ grub_arm_thm_jump19_set_offset ((grub_uint16_t *) target, offset);
+ }
+ break;
+ default:
+ return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
+ N_("relocation 0x%x is not implemented yet"),
+ ELF_R_TYPE (rel->r_info));
+ }
+ }
+
+ return GRUB_ERR_NONE;
+}
+
+
+/*
+ * 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. */
+ if (e->e_ident[EI_CLASS] != ELFCLASS32
+ || e->e_ident[EI_DATA] != ELFDATA2LSB || e->e_machine != EM_ARM)
+ return grub_error (GRUB_ERR_BAD_OS,
+ N_("invalid arch-dependent ELF magic"));
+
+ return GRUB_ERR_NONE;
+}
diff --git a/grub-core/kern/arm/dl_helper.c b/grub-core/kern/arm/dl_helper.c
new file mode 100644
index 0000000..21d77f7
--- /dev/null
+++ b/grub-core/kern/arm/dl_helper.c
@@ -0,0 +1,245 @@
+/* dl.c - arch-dependent part of loadable module support */
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2013 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/mm.h>
+#include <grub/i18n.h>
+#include <grub/arm/reloc.h>
+
+static inline grub_uint32_t
+thumb_get_instruction_word (grub_uint16_t *target)
+{
+ /* Extract instruction word in alignment-safe manner */
+ return grub_le_to_cpu16 ((*target)) << 16 | grub_le_to_cpu16 (*(target + 1));
+}
+
+static inline void
+thumb_set_instruction_word (grub_uint16_t *target, grub_uint32_t insword)
+{
+ *target = grub_cpu_to_le16 (insword >> 16);
+ *(target + 1) = grub_cpu_to_le16 (insword & 0xffff);
+}
+
+/*
+ * R_ARM_ABS32
+ *
+ * Simple relocation of 32-bit value (in literal pool)
+ */
+grub_err_t
+grub_arm_reloc_abs32 (Elf32_Word *target, Elf32_Addr sym_addr)
+{
+ Elf32_Addr tmp;
+
+ tmp = grub_le_to_cpu32 (*target);
+ tmp += sym_addr;
+ *target = grub_cpu_to_le32 (tmp);
+
+ return GRUB_ERR_NONE;
+}
+
+/********************************************************************
+ * Thumb (T32) relocations: *
+ * *
+ * 32-bit Thumb instructions can be 16-bit aligned, and are fetched *
+ * little-endian, requiring some additional fiddling. *
+ ********************************************************************/
+
+grub_int32_t
+grub_arm_thm_call_get_offset (grub_uint16_t *target)
+{
+ grub_uint32_t sign, j1, j2;
+ grub_uint32_t insword;
+ grub_int32_t offset;
+
+ insword = thumb_get_instruction_word (target);
+
+ /* Extract bitfields from instruction words */
+ sign = (insword >> 26) & 1;
+ j1 = (insword >> 13) & 1;
+ j2 = (insword >> 11) & 1;
+ offset = (sign << 24) | ((~(j1 ^ sign) & 1) << 23) |
+ ((~(j2 ^ sign) & 1) << 22) |
+ ((insword & 0x03ff0000) >> 4) | ((insword & 0x000007ff) << 1);
+
+ /* Sign adjust and calculate offset */
+ if (offset & (1 << 24))
+ offset -= (1 << 25);
+
+ return offset;
+}
+
+grub_err_t
+grub_arm_thm_call_set_offset (grub_uint16_t *target, grub_int32_t offset)
+{
+ grub_uint32_t sign, j1, j2;
+ const grub_uint32_t insmask = 0xf800d000;
+ grub_uint32_t insword;
+ int is_blx;
+
+ insword = thumb_get_instruction_word (target);
+
+ if (((insword >> 12) & 0xd) == 0xc)
+ is_blx = 1;
+ else
+ is_blx = 0;
+
+ if (!is_blx && !(offset & 1))
+ return grub_error (GRUB_ERR_BAD_MODULE, "bl/b.w targettting ARM");
+
+ /* Transform blx into bl if necessarry. */
+ if (is_blx && (offset & 1))
+ insword |= (1 << 12);
+
+ /* Reassemble instruction word */
+ sign = (offset >> 24) & 1;
+ j1 = sign ^ (~(offset >> 23) & 1);
+ j2 = sign ^ (~(offset >> 22) & 1);
+ insword = (insword & insmask) |
+ (sign << 26) |
+ (((offset >> 12) & 0x03ff) << 16) |
+ (j1 << 13) | (j2 << 11) | ((offset >> 1) & 0x07ff);
+
+ thumb_set_instruction_word (target, insword);
+
+ grub_dprintf ("dl", " *insword = 0x%08x", insword);
+
+ return GRUB_ERR_NONE;
+}
+
+grub_int32_t
+grub_arm_thm_jump19_get_offset (grub_uint16_t *target)
+{
+ grub_int32_t offset;
+ grub_uint32_t insword;
+
+ insword = thumb_get_instruction_word (target);
+
+ /* Extract and sign extend offset */
+ offset = ((insword >> 26) & 1) << 19
+ | ((insword >> 11) & 1) << 18
+ | ((insword >> 13) & 1) << 17
+ | ((insword >> 16) & 0x3f) << 11
+ | (insword & 0x7ff);
+ offset <<= 1;
+ if (offset & (1 << 20))
+ offset -= (1 << 21);
+
+ return offset;
+}
+
+void
+grub_arm_thm_jump19_set_offset (grub_uint16_t *target, grub_int32_t offset)
+{
+ grub_uint32_t insword;
+ const grub_uint32_t insmask = 0xfbc0d000;
+
+ offset >>= 1;
+ offset &= 0xfffff;
+
+ insword = thumb_get_instruction_word (target);
+
+ /* Reassemble instruction word and write back */
+ insword &= insmask;
+ insword |= ((offset >> 19) & 1) << 26
+ | ((offset >> 18) & 1) << 11
+ | ((offset >> 17) & 1) << 13
+ | ((offset >> 11) & 0x3f) << 16
+ | (offset & 0x7ff);
+ thumb_set_instruction_word (target, insword);
+}
+
+int
+grub_arm_thm_jump19_check_offset (grub_int32_t offset)
+{
+ if ((offset > 1048574) || (offset < -1048576))
+ return 0;
+ return 1;
+}
+
+grub_uint16_t
+grub_arm_thm_movw_movt_get_value (grub_uint16_t *target)
+{
+ grub_uint32_t insword;
+
+ insword = thumb_get_instruction_word (target);
+
+ return ((insword & 0xf0000) >> 4) | ((insword & 0x04000000) >> 15) | \
+ ((insword & 0x7000) >> 4) | (insword & 0xff);
+}
+
+void
+grub_arm_thm_movw_movt_set_value (grub_uint16_t *target, grub_uint16_t value)
+{
+ grub_uint32_t insword;
+ const grub_uint32_t insmask = 0xfbf08f00;
+
+ insword = thumb_get_instruction_word (target);
+ insword &= insmask;
+
+ insword |= ((value & 0xf000) << 4) | ((value & 0x0800) << 15) | \
+ ((value & 0x0700) << 4) | (value & 0xff);
+
+ thumb_set_instruction_word (target, insword);
+}
+
+
+/***********************************************************
+ * ARM (A32) relocations: *
+ * *
+ * ARM instructions are 32-bit in size and 32-bit aligned. *
+ ***********************************************************/
+
+grub_int32_t
+grub_arm_jump24_get_offset (grub_uint32_t *target)
+{
+ grub_int32_t offset;
+ grub_uint32_t insword;
+
+ insword = grub_le_to_cpu32 (*target);
+
+ offset = (insword & 0x00ffffff) << 2;
+ if (offset & 0x02000000)
+ offset -= 0x04000000;
+ return offset;
+}
+
+int
+grub_arm_jump24_check_offset (grub_int32_t offset)
+{
+ if (offset >= 0x02000000 || offset < -0x02000000)
+ return 0;
+ return 1;
+}
+
+void
+grub_arm_jump24_set_offset (grub_uint32_t *target,
+ grub_int32_t offset)
+{
+ grub_uint32_t insword;
+
+ insword = grub_le_to_cpu32 (*target);
+
+ insword &= 0xff000000;
+ insword |= (offset >> 2) & 0x00ffffff;
+
+ *target = grub_cpu_to_le32 (insword);
+}
diff --git a/grub-core/kern/arm/efi/init.c b/grub-core/kern/arm/efi/init.c
new file mode 100644
index 0000000..40c3b46
--- /dev/null
+++ b/grub-core/kern/arm/efi/init.c
@@ -0,0 +1,77 @@
+/* init.c - initialize an arm-based EFI system */
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2013 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/env.h>
+#include <grub/kernel.h>
+#include <grub/misc.h>
+#include <grub/mm.h>
+#include <grub/time.h>
+#include <grub/efi/efi.h>
+#include <grub/loader.h>
+
+static grub_uint64_t tmr;
+static grub_efi_event_t tmr_evt;
+
+static grub_uint64_t
+grub_efi_get_time_ms (void)
+{
+ return tmr;
+}
+
+static void
+increment_timer (grub_efi_event_t event __attribute__ ((unused)),
+ void *context __attribute__ ((unused)))
+{
+ tmr += 10;
+}
+
+void
+grub_machine_init (void)
+{
+ grub_efi_boot_services_t *b;
+
+ grub_efi_init ();
+
+ b = grub_efi_system_table->boot_services;
+
+ efi_call_5 (b->create_event, GRUB_EFI_EVT_TIMER | GRUB_EFI_EVT_NOTIFY_SIGNAL,
+ GRUB_EFI_TPL_CALLBACK, increment_timer, NULL, &tmr_evt);
+ efi_call_3 (b->set_timer, tmr_evt, GRUB_EFI_TIMER_PERIODIC, 100000);
+
+ grub_install_get_time_ms (grub_efi_get_time_ms);
+}
+
+void
+grub_machine_fini (int flags)
+{
+ grub_efi_boot_services_t *b;
+
+ if (!(flags & GRUB_LOADER_FLAG_NORETURN))
+ return;
+
+ b = grub_efi_system_table->boot_services;
+
+ efi_call_3 (b->set_timer, tmr_evt, GRUB_EFI_TIMER_CANCEL, 0);
+ efi_call_1 (b->close_event, tmr_evt);
+
+ grub_efi_fini ();
+
+ if (!(flags & GRUB_LOADER_FLAG_EFI_KEEP_ALLOCATED_MEMORY))
+ grub_efi_memory_fini ();
+}
diff --git a/grub-core/kern/arm/efi/startup.S b/grub-core/kern/arm/efi/startup.S
new file mode 100644
index 0000000..9f82653
--- /dev/null
+++ b/grub-core/kern/arm/efi/startup.S
@@ -0,0 +1,36 @@
+/*
+ * (C) Copyright 2013 Free Software Foundation
+ *
+ * This program 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 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ *
+ */
+
+#include <grub/symbol.h>
+
+ .file "startup.S"
+ .text
+ .arm
+FUNCTION(_start)
+ /*
+ * EFI_SYSTEM_TABLE and EFI_HANDLE are passed in r1/r0.
+ */
+ ldr ip, =EXT_C(grub_efi_image_handle)
+ str r0, [ip]
+ ldr ip, =EXT_C(grub_efi_system_table)
+ str r1, [ip]
+ ldr ip, =EXT_C(grub_main)
+ bx ip
+ END
diff --git a/grub-core/kern/arm/startup.S b/grub-core/kern/arm/startup.S
new file mode 100644
index 0000000..3946fe8
--- /dev/null
+++ b/grub-core/kern/arm/startup.S
@@ -0,0 +1,177 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2013 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/offsets.h>
+#include <grub/symbol.h>
+#include <grub/machine/kernel.h>
+
+/*
+ * GRUB is called from U-Boot as a Linux Kernel type image, which
+ * means among other things that it always enters in ARM state.
+ *
+ * coreboot starts in ARM mode as well.
+ *
+ * Overview of GRUB image layout:
+ *
+ * _start:
+ * Entry point (1 ARM branch instruction, to "codestart")
+ * grub_total_module_size:
+ * Data field: Size of included module blob
+ * (when generated by grub-mkimage)
+ * codestart:
+ * Remainder of statically-linked executable code and data.
+ * __bss_start:
+ * Start of included module blob.
+ * Also where global/static variables are located.
+ * _end:
+ * End of bss region (but not necessarily module blob).
+ * <stack>:
+ * <modules>:
+ * Loadable modules, post relocation.
+ * <heap>:
+ */
+
+ .text
+ .arm
+FUNCTION(_start)
+ b codestart
+
+ @ Size of final image integrated module blob - set by grub-mkimage
+ .org _start + GRUB_KERNEL_MACHINE_TOTAL_MODULE_SIZE
+VARIABLE(grub_total_module_size)
+ .long 0
+
+VARIABLE(grub_modbase)
+ .long 0
+bss_start_ptr:
+ .long EXT_C(__bss_start)
+end_ptr:
+ .long EXT_C(_end)
+
+ @ Memory map at start:
+ @ * text+data
+ @ * list relocations
+ @ * modules
+ @ Before we enter C, we need to apply the relocations
+ @ and get following map:
+ @ * text+data
+ @ * BSS (cleared)
+ @ * stack
+ @ * modules
+ @
+ @ To make things easier we ensure
+ @ that BSS+stack is larger than list of relocations
+ @ by increasing stack if necessarry.
+ @ This allows us to always unconditionally copy backwards
+ @ Currently list of relocations is ~5K and stack is set
+ @ to be at least 256K
+
+FUNCTION(codestart)
+ @ Store context: Machine ID, atags/dtb, ...
+ @ U-Boot API signature is stored on the U-Boot heap
+ @ Stack pointer used as start address for signature probing
+ mov r12, sp
+ adr sp, entry_state
+ push {r0-r12,lr} @ store U-Boot context (sp in r12)
+
+ adr r1, _start
+ ldr r0, bss_start_ptr @ src
+ add r0, r0, r1
+
+ add r0, r0, #(GRUB_KERNEL_MACHINE_MOD_ALIGN - 1)
+ mvn r2, #(GRUB_KERNEL_MACHINE_MOD_ALIGN - 1)
+ and r0, r0, r2
+1:
+ ldr r3, [r0], #4 @load next offset
+ @ both -2 and -1 are treated the same as we have only one type of relocs
+ @ -2 means "end of this type of relocs" and -1 means "end of all relocs"
+ add r2, r3, #2
+ cmp r2, #1
+ bls reloc_done
+ @ Adjust next offset
+ ldr r2, [r3, r1]
+ add r2, r2, r1
+ str r2, [r3, r1]
+ b 1b
+
+reloc_done:
+
+ @ Modules have been stored as a blob
+ @ they need to be manually relocated to _end
+ add r0, r0, #(GRUB_KERNEL_MACHINE_MOD_ALIGN - 1)
+ mvn r1, #(GRUB_KERNEL_MACHINE_MOD_ALIGN - 1)
+ and r0, r0, r1 @ src = aligned end of relocations
+
+ ldr r1, end_ptr @ dst = End of BSS
+ ldr r2, grub_total_module_size @ blob size
+
+ add r1, r1, #GRUB_KERNEL_MACHINE_STACK_SIZE
+ and r1, r1, #~0x7 @ Ensure 8-byte alignment
+
+ sub sp, r1, #8
+ add r1, r1, #1024
+
+ str r1, EXT_C(grub_modbase)
+
+ /* Coreboot already places modules at right place. */
+#ifndef GRUB_MACHINE_COREBOOT
+ add r1, r1, r2
+ add r0, r0, r2
+ sub r1, r1, #4
+ sub r0, r0, #4
+
+1: ldr r3, [r0], #-4 @ r3 = *src--
+ str r3, [r1], #-4 @ *dst-- = r3
+ subs r2, #4 @ remaining -= 4
+ bne 1b @ while remaining != 0
+#endif
+
+ @ Since we _are_ the C run-time, we need to manually zero the BSS
+ @ region before continuing
+ ldr r0, bss_start_ptr @ zero from here
+ @ If unaligned, bytewise zero until base address aligned.
+ mov r2, #0
+1: tst r0, #3
+ beq 2f
+ strb r2, [r0], #1
+ b 1b
+2: ldr r1, end_ptr @ to here
+1: str r2, [r0], #4
+ cmp r0, r1
+ bne 1b
+
+ b EXT_C(grub_main)
+
+ .align 3
+@ U-boot/coreboot context stack space
+VARIABLE(grub_arm_saved_registers)
+ .long 0 @ r0
+ .long 0 @ r1
+ .long 0 @ r2
+ .long 0 @ r3
+ .long 0 @ r4
+ .long 0 @ r5
+ .long 0 @ r6
+ .long 0 @ r7
+ .long 0 @ r8
+ .long 0 @ r9
+ .long 0 @ r10
+ .long 0 @ r11
+ .long 0 @ sp
+ .long 0 @ lr
+entry_state:
diff --git a/grub-core/kern/arm/uboot/init.c b/grub-core/kern/arm/uboot/init.c
new file mode 100644
index 0000000..2a6aa3f
--- /dev/null
+++ b/grub-core/kern/arm/uboot/init.c
@@ -0,0 +1,70 @@
+/* init.c - generic U-Boot initialization and finalization */
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2016 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/uboot/uboot.h>
+#include <grub/arm/startup.h>
+#include <grub/uboot/api_public.h>
+
+extern int (*grub_uboot_syscall_ptr) (int, int *, ...);
+
+grub_uint32_t
+grub_uboot_get_machine_type (void)
+{
+ return grub_arm_saved_registers.r[1];
+}
+
+grub_addr_t
+grub_uboot_get_boot_data (void)
+{
+ return grub_arm_saved_registers.r[2];
+}
+
+int
+grub_uboot_api_init (void)
+{
+ struct api_signature *start, *end;
+ struct api_signature *p;
+ grub_addr_t grub_uboot_search_hint = grub_arm_saved_registers.sp;
+ if (grub_uboot_search_hint)
+ {
+ /* Extended search range to work around Trim Slice U-Boot issue */
+ start = (struct api_signature *) ((grub_uboot_search_hint & ~0x000fffff)
+ - 0x00500000);
+ end =
+ (struct api_signature *) ((grub_addr_t) start + UBOOT_API_SEARCH_LEN -
+ API_SIG_MAGLEN + 0x00500000);
+ }
+ else
+ {
+ start = 0;
+ end = (struct api_signature *) (256 * 1024 * 1024);
+ }
+
+ /* Structure alignment is (at least) 8 bytes */
+ for (p = start; p < end; p = (void *) ((grub_addr_t) p + 8))
+ {
+ if (grub_memcmp (&(p->magic), API_SIG_MAGIC, API_SIG_MAGLEN) == 0)
+ {
+ grub_uboot_syscall_ptr = p->syscall;
+ return p->version;
+ }
+ }
+
+ return 0;
+}
diff --git a/grub-core/kern/arm/uboot/uboot.S b/grub-core/kern/arm/uboot/uboot.S
new file mode 100644
index 0000000..d128775
--- /dev/null
+++ b/grub-core/kern/arm/uboot/uboot.S
@@ -0,0 +1,73 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2013 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/offsets.h>
+#include <grub/symbol.h>
+#include <grub/machine/kernel.h>
+
+ /*
+ * uboot_syscall():
+ * This function is effectively a veneer, so it cannot
+ * modify the stack or corrupt any registers other than
+ * r12 (ip). Furthermore it needs to restore r8 for
+ * U-Boot (Global Data Pointer) and preserve it for Grub.
+ */
+FUNCTION(grub_uboot_syscall)
+ str r8, transition_space
+ str lr, transition_space + 4
+ str r9, transition_space + 8
+
+ ldr ip, saved_registers_ptr
+ ldr r8, [ip, #4 * 8]
+ ldr r9, [ip, #4 * 9]
+
+ bl do_syscall
+
+ ldr r8, transition_space
+ ldr lr, transition_space + 4
+ ldr r9, transition_space + 8
+
+ bx lr
+do_syscall:
+
+ ldr ip, grub_uboot_syscall_ptr
+ bx ip
+
+FUNCTION(grub_uboot_return)
+ ldr ip, saved_registers_ptr
+ ldr sp, [ip, #4 * 4]
+ pop {r4-r12, lr}
+ mov sp, r12
+ bx lr
+
+
+ .align 3
+
+@ GRUB context stack space
+transition_space:
+ .long 0 @ r8
+ .long 0 @ lr
+ .long 0 @ r9
+
+saved_registers_ptr:
+ .long EXT_C(grub_arm_saved_registers)
+
+VARIABLE(grub_uboot_syscall_ptr)
+ .long 0 @
+
+ END