Initial import of Leif's work

This commit is contained in:
Leif Lindholm 2013-04-07 02:41:07 +02:00 committed by Vladimir 'phcoder' Serbinenko
parent 21026747df
commit 389b31cd71
65 changed files with 7311 additions and 13 deletions

251
grub-core/kern/arm/cache.S Normal file
View file

@ -0,0 +1,251 @@
/*
* 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 "cache.S"
.text
.syntax unified
#if !defined (__thumb2__)
.arm
#define ARM(x...) x
#define THUMB(x...)
#else
.thumb
#define THUMB(x...) x
#define ARM(x...)
#endif
.align 2
/*
* Simple cache maintenance functions
*/
@ r0 - *beg (inclusive)
@ r1 - *end (exclusive)
clean_dcache_range:
@ Clean data cache range for range to point-of-unification
ldr r2, dlinesz
1: cmp r0, r1
bge 2f
#ifdef DEBUG
push {r0-r2, lr}
mov r1, r2
mov r2, r0
ldr r0, =dcstr
bl EXT_C(grub_printf)
pop {r0-r2, lr}
#endif
mcr p15, 0, r0, c7, c11, 1 @ DCCMVAU
add r0, r0, r2 @ Next line
b 1b
2: dsb
bx lr
@ r0 - *beg (inclusive)
@ r1 - *end (exclusive)
invalidate_icache_range:
@ Invalidate instruction cache for range to point-of-unification
ldr r2, ilinesz
1: cmp r0, r1
bge 2f
#ifdef DEBUG
push {r0-r2, lr}
mov r1, r2
mov r2, r0
ldr r0, =icstr
bl EXT_C(grub_printf)
pop {r0-r2, lr}
#endif
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
@void __wrap___clear_cache(char *beg, char *end);
FUNCTION(__wrap___clear_cache)
dmb
dsb
push {r4-r6, lr}
ldr r2, probed @ If first call, probe cache sizes
cmp r2, #0
bleq probe_caches @ This call corrupts r3
mov r4, r0
mov r5, r1
bl clean_dcache_range
mov r0, r4
mov r1, r5
bl invalidate_icache_range
pop {r4-r6, pc}
probe_caches:
push {r4-r6, lr}
mrc p15, 0, r4, c0, c0, 1 @ Read Cache Type Register
mov r5, #1
ubfx r6, r4, #16, #4 @ Extract min D-cache num word log2
add r6, r6, #2 @ words->bytes
lsl r6, r5, r6 @ Convert to num bytes
ldr r3, =dlinesz
str r6, [r3]
and r6, r4, #0xf @ Extract min I-cache num word log2
add r6, r6, #2 @ words->bytes
lsl r6, r5, r6 @ Convert to num bytes
ldr r3, =ilinesz
str r6, [r3]
ldr r3, =probed @ Flag cache probing done
str r5, [r3]
pop {r4-r6, pc}
#ifdef DEBUG
dcstr: .asciz "cleaning %d bytes of D cache @ 0x%08x\n"
icstr: .asciz "invalidating %d bytes of I cache @ 0x%08x\n"
#endif
.align 3
probed: .long 0
dlinesz:
.long 0
ilinesz:
.long 0
@void grub_arch_sync_caches (void *address, grub_size_t len)
FUNCTION(grub_arch_sync_caches)
add r1, r0, r1
b __wrap___clear_cache
@ 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
ubfx r1, r0, #24, #3 @ Extract LoC
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 CSSIDR 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 CSSIDR
ubfx r3, r8, #13, #14 @ Number of sets -1
ubfx r4, r8, #3, #9 @ Number of ways -1
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 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, pc}
FUNCTION(grub_arm_disable_caches_mmu)
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, pc}

490
grub-core/kern/arm/dl.c Normal file
View file

@ -0,0 +1,490 @@
/* 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>
#ifdef GRUB_UTIL
# include <grub/util/misc.h>
#else
# if !defined(__thumb2__)
# error "Relocations not implemented for A32 ("ARM") instruction set yet!"
# endif
grub_err_t reloc_jump24 (grub_uint32_t *addr, Elf32_Addr sym_addr);
grub_err_t reloc_thm_call (grub_uint16_t *addr, Elf32_Addr sym_addr);
grub_err_t reloc_thm_jump19 (grub_uint16_t *addr, Elf32_Addr sym_addr);
#ifdef DL_DEBUG
static const char *symstrtab;
/*
* This is a bit of a hack, setting the symstrtab pointer to the last STRTAB
* section in the module (which is where symbol names are in the objects I've
* inspected manually).
*/
static void
set_symstrtab (Elf_Ehdr * e)
{
int i;
Elf_Shdr *s;
symstrtab = NULL;
for (i = 0, s = (Elf_Shdr *) ((grub_uint32_t) e + e->e_shoff);
i < e->e_shnum;
i++, s = (Elf_Shdr *) ((grub_uint32_t) s + e->e_shentsize))
if (s->sh_type == SHT_STRTAB)
symstrtab = (void *) ((grub_addr_t) e + s->sh_offset);
}
static const char *
get_symbolname (Elf_Sym * sym)
{
const char *symbolname = symstrtab + sym->st_name;
return (*symbolname ? symbolname : NULL);
}
#endif /* DL_DEBUG */
/*
* R_ARM_ABS32
*
* Simple relocation of 32-bit value (in literal pool)
*/
static grub_err_t
reloc_abs32 (Elf_Word *target, Elf_Addr sym_addr)
{
Elf_Addr tmp;
tmp = *target;
tmp += sym_addr;
*target = tmp;
#if 0 //def GRUB_UTIL
grub_util_info (" %s: reloc_abs32 0x%08x => 0x%08x", __FUNCTION__,
(unsigned int) sym_addr, (unsigned int) tmp);
#endif
return GRUB_ERR_NONE;
}
#endif /* ndef GRUB_UTIL */
/********************************************************************
* Thumb (T32) relocations: *
* *
* 32-bit Thumb instructions can be 16-bit aligned, and are fetched *
* little-endian, requiring some additional fiddling. *
********************************************************************/
/*
* R_ARM_THM_CALL/THM_JUMP24
*
* Relocate Thumb (T32) instruction set relative branches:
* B.W, BL and BLX
*/
grub_err_t
reloc_thm_call (grub_uint16_t *target, Elf32_Addr sym_addr)
{
grub_int32_t offset, offset_low, offset_high;
grub_uint32_t sign, j1, j2, is_blx;
grub_uint32_t insword, insmask;
/* Extract instruction word in alignment-safe manner */
insword = (*target << 16) | (*(target + 1));
insmask = 0xf800d000;
/* B.W/BL or BLX? Affects range and expected target state */
if (((insword >> 12) & 0xd) == 0xc)
is_blx = 1;
else
is_blx = 0;
/* If BLX, target symbol must be ARM (target address LSB == 0) */
if (is_blx && (sym_addr & 1))
{
#ifndef GRUB_UTIL
return grub_error
(GRUB_ERR_BUG, N_("Relocation targeting wrong execution state"));
#else
grub_util_error ("Relocation targeting wrong execution state");
#endif
}
offset_low = -16777216;
offset_high = is_blx ? 16777212 : 16777214;
/* 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);
#ifdef GRUB_UTIL
grub_util_info (" sym_addr = 0x%08x", sym_addr);
#endif
#ifdef GRUB_UTIL
offset += sym_addr;
#else
offset += sym_addr - (grub_uint32_t) target;
#endif
#ifdef DEBUG
grub_printf(" %s: target=0x%08x, sym_addr=0x%08x, offset=%d\n",
is_blx ? "BLX" : "BL", (unsigned int) target, sym_addr, offset);
#endif
if ((offset < offset_low) || (offset > offset_high))
{
#ifdef GRUB_UTIL
grub_util_error ("Relocation out of range");
#else
return grub_error
(GRUB_ERR_OUT_OF_RANGE, N_("THM_CALL Relocation out of range."));
#endif
}
#ifdef GRUB_UTIL
grub_util_info (" relative destination = 0x%08x",
(unsigned int)target + offset);
#endif
/* 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);
/* Write instruction word back in alignment-safe manner */
*target = (insword >> 16) & 0xffff;
*(target + 1) = insword & 0xffff;
#ifdef GRUB_UTIL
#pragma GCC diagnostic ignored "-Wcast-align"
grub_util_info (" *target = 0x%08x", *((unsigned int *)target));
#endif
return GRUB_ERR_NONE;
}
/*
* R_ARM_THM_JUMP19
*
* Relocate conditional Thumb (T32) B<c>.W
*/
grub_err_t
reloc_thm_jump19 (grub_uint16_t *addr, Elf32_Addr sym_addr)
{
grub_int32_t offset;
grub_uint32_t insword, insmask;
/* Extract instruction word in alignment-safe manner */
insword = (*addr) << 16 | *(addr + 1);
insmask = 0xfbc0d800;
/* Extract and sign extend offset */
offset = ((insword >> 26) & 1) << 18
| ((insword >> 11) & 1) << 17
| ((insword >> 13) & 1) << 16
| ((insword >> 16) & 0x3f) << 11
| (insword & 0x7ff);
offset <<= 1;
if (offset & (1 << 19))
offset -= (1 << 20);
/* Adjust and re-truncate offset */
#ifdef GRUB_UTIL
offset += sym_addr;
#else
offset += sym_addr - (grub_uint32_t) addr;
#endif
if ((offset > 1048574) || (offset < -1048576))
{
return grub_error
(GRUB_ERR_OUT_OF_RANGE, N_("THM_JUMP19 Relocation out of range."));
}
offset >>= 1;
offset &= 0x7ffff;
/* Reassemble instruction word and write back */
insword &= insmask;
insword |= ((offset >> 18) & 1) << 26
| ((offset >> 17) & 1) << 11
| ((offset >> 16) & 1) << 13
| ((offset >> 11) & 0x3f) << 16
| (offset & 0x7ff);
*addr = insword >> 16;
*(addr + 1) = insword & 0xffff;
return GRUB_ERR_NONE;
}
/***********************************************************
* ARM (A32) relocations: *
* *
* ARM instructions are 32-bit in size and 32-bit aligned. *
***********************************************************/
/*
* R_ARM_JUMP24
*
* Relocate ARM (A32) B
*/
grub_err_t
reloc_jump24 (grub_uint32_t *addr, Elf32_Addr sym_addr)
{
grub_uint32_t insword;
grub_int32_t offset;
insword = *addr;
offset = (insword & 0x00ffffff) << 2;
if (offset & 0x02000000)
offset -= 0x04000000;
#ifdef GRUB_UTIL
offset += sym_addr;
#else
offset += sym_addr - (grub_uint32_t) addr;
#endif
insword &= 0xff000000;
insword |= (offset >> 2) & 0x00ffffff;
*addr = insword;
return GRUB_ERR_NONE;
}
/*************************************************
* Runtime dynamic linker with helper functions. *
*************************************************/
#ifndef GRUB_UTIL
/*
* find_segment(): finds a module segment matching sh_info
*/
static grub_dl_segment_t
find_segment (grub_dl_segment_t seg, Elf32_Word sh_info)
{
for (; seg; seg = seg->next)
if (seg->section == sh_info)
return seg;
return NULL;
}
/*
* do_relocations():
* Iterate over all relocations in section, calling appropriate functions
* for patching.
*/
static grub_err_t
do_relocations (Elf_Shdr * relhdr, Elf_Ehdr * e, grub_dl_t mod)
{
grub_dl_segment_t seg;
Elf_Rel *rel;
Elf_Sym *sym;
int i, entnum;
entnum = relhdr->sh_size / sizeof (Elf_Rel);
/* Find the target segment for this relocation section. */
seg = find_segment (mod->segment, relhdr->sh_info);
if (!seg)
return grub_error (GRUB_ERR_EOF, N_("relocation segment not found"));
rel = (Elf_Rel *) ((grub_addr_t) e + relhdr->sh_offset);
/* Step through all relocations */
for (i = 0, sym = mod->symtab; i < entnum; i++)
{
Elf_Addr *target, sym_addr;
int relsym, reltype;
grub_err_t retval;
if (seg->size < rel[i].r_offset)
return grub_error (GRUB_ERR_BAD_MODULE,
"reloc offset is out of the segment");
relsym = ELF_R_SYM (rel[i].r_info);
reltype = ELF_R_TYPE (rel[i].r_info);
target = (Elf_Word *) ((grub_addr_t) seg->addr + rel[i].r_offset);
sym_addr = sym[relsym].st_value;
#ifdef DL_DEBUG
grub_printf ("%s: 0x%08x -> %s @ 0x%08x\n", __FUNCTION__,
(grub_addr_t) sym_addr, get_symbolname (sym), sym->st_value);
#endif
switch (reltype)
{
case R_ARM_ABS32:
{
/* Data will be naturally aligned */
retval = reloc_abs32 (target, sym_addr);
if (retval != GRUB_ERR_NONE)
return retval;
}
break;
case R_ARM_JUMP24:
{
retval = reloc_jump24 (target, sym_addr);
if (retval != GRUB_ERR_NONE)
return retval;
}
break;
case R_ARM_THM_CALL:
case R_ARM_THM_JUMP24:
{
/* Thumb instructions can be 16-bit aligned */
retval = reloc_thm_call ((grub_uint16_t *) target, sym_addr);
if (retval != GRUB_ERR_NONE)
return retval;
}
break;
case R_ARM_THM_JUMP19:
{
/* Thumb instructions can be 16-bit aligned */
retval = reloc_thm_jump19 ((grub_uint16_t *) target, sym_addr);
if (retval != GRUB_ERR_NONE)
return retval;
}
break;
default:
return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
N_("relocation 0x%x is not implemented yet"),
reltype);
}
}
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;
}
/*
* Verify that provided ELF header contains reference to a symbol table
*/
static int
has_symtab (Elf_Ehdr * e)
{
int i;
Elf_Shdr *s;
for (i = 0, s = (Elf_Shdr *) ((grub_uint32_t) e + e->e_shoff);
i < e->e_shnum;
i++, s = (Elf_Shdr *) ((grub_uint32_t) s + e->e_shentsize))
if (s->sh_type == SHT_SYMTAB)
return 1;
return 0;
}
/*
* grub_arch_dl_relocate_symbols():
* Only externally visible function in this file.
* Locates the relocations section of the ELF object, and calls
* do_relocations() to deal with it.
*/
grub_err_t
grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr)
{
Elf_Ehdr *e = ehdr;
Elf_Shdr *s;
unsigned i;
if (!has_symtab (e))
return grub_error (GRUB_ERR_BAD_MODULE, N_("no symbol table"));
#ifdef DL_DEBUG
set_symstrtab (e);
#endif
#define FIRST_SHDR(x) ((Elf_Shdr *) ((grub_addr_t)(x) + (x)->e_shoff))
#define NEXT_SHDR(x, y) ((Elf_Shdr *) ((grub_addr_t)(y) + (x)->e_shentsize))
for (i = 0, s = FIRST_SHDR (e); i < e->e_shnum; i++, s = NEXT_SHDR (e, s))
{
grub_err_t ret;
switch (s->sh_type)
{
case SHT_REL:
{
/* Relocations, no addends */
ret = do_relocations (s, e, mod);
if (ret != GRUB_ERR_NONE)
return ret;
}
break;
case SHT_NULL:
case SHT_PROGBITS:
case SHT_SYMTAB:
case SHT_STRTAB:
case SHT_NOBITS:
case SHT_ARM_ATTRIBUTES:
break;
case SHT_RELA:
default:
{
grub_printf ("unhandled section_type: %d (0x%08x)\n",
s->sh_type, s->sh_type);
return GRUB_ERR_NOT_IMPLEMENTED_YET;
};
}
}
#undef FIRST_SHDR
#undef NEXT_SHDR
return GRUB_ERR_NONE;
}
#endif /* ndef GRUB_UTIL */

View file

@ -0,0 +1,68 @@
/* 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>
/*
* A bit ugly, but functional - and should be completely portable.
*/
static grub_uint64_t
grub_efi_get_time_ms(void)
{
grub_efi_time_t now;
grub_uint64_t retval;
grub_efi_status_t status;
status = efi_call_2 (grub_efi_system_table->runtime_services->get_time,
&now, NULL);
if (status != GRUB_EFI_SUCCESS)
{
grub_printf("No time!\n");
return 0;
}
retval = now.year * 365 * 24 * 60 * 60 * 1000;
retval += now.month * 30 * 24 * 60 * 60 * 1000;
retval += now.day * 24 * 60 * 60 * 1000;
retval += now.hour * 60 * 60 * 1000;
retval += now.minute * 60 * 1000;
retval += now.second * 1000;
retval += now.nanosecond / 1000;
grub_dprintf("timer", "timestamp: 0x%llx\n", retval);
return retval;
}
void
grub_machine_init (void)
{
grub_efi_init ();
grub_install_get_time_ms (grub_efi_get_time_ms);
}
void
grub_machine_fini (void)
{
grub_efi_fini ();
}

View file

@ -0,0 +1,203 @@
/* misc.c - various system functions for 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/misc.h>
#include <grub/mm.h>
#include <grub/cpu/linux.h>
#include <grub/cpu/system.h>
#include <grub/efi/efi.h>
#include <grub/machine/loader.h>
static inline grub_size_t
page_align (grub_size_t size)
{
return (size + (1 << 12) - 1) & (~((1 << 12) - 1));
}
/* Find the optimal number of pages for the memory map. Is it better to
move this code to efi/mm.c? */
static grub_efi_uintn_t
find_mmap_size (void)
{
static grub_efi_uintn_t mmap_size = 0;
if (mmap_size != 0)
return mmap_size;
mmap_size = (1 << 12);
while (1)
{
int ret;
grub_efi_memory_descriptor_t *mmap;
grub_efi_uintn_t desc_size;
mmap = grub_malloc (mmap_size);
if (! mmap)
return 0;
ret = grub_efi_get_memory_map (&mmap_size, mmap, 0, &desc_size, 0);
grub_free (mmap);
if (ret < 0)
{
grub_error (GRUB_ERR_IO, "cannot get memory map");
return 0;
}
else if (ret > 0)
break;
mmap_size += (1 << 12);
}
/* Increase the size a bit for safety, because GRUB allocates more on
later, and EFI itself may allocate more. */
mmap_size += (1 << 12);
return page_align (mmap_size);
}
#define NEXT_MEMORY_DESCRIPTOR(desc, size) \
((grub_efi_memory_descriptor_t *) ((char *) (desc) + (size)))
#define PAGE_SHIFT 12
void *
grub_efi_allocate_loader_memory (grub_uint32_t min_offset, grub_uint32_t size)
{
grub_efi_uintn_t desc_size;
grub_efi_memory_descriptor_t *mmap, *mmap_end;
grub_efi_uintn_t mmap_size, tmp_mmap_size;
grub_efi_memory_descriptor_t *desc;
void *mem = NULL;
grub_addr_t min_start = 0;
mmap_size = find_mmap_size();
if (!mmap_size)
return NULL;
mmap = grub_malloc(mmap_size);
if (!mmap)
return NULL;
tmp_mmap_size = mmap_size;
if (grub_efi_get_memory_map (&tmp_mmap_size, mmap, 0, &desc_size, 0) <= 0)
{
grub_error (GRUB_ERR_IO, "cannot get memory map");
goto fail;
}
mmap_end = NEXT_MEMORY_DESCRIPTOR (mmap, tmp_mmap_size);
/* Find lowest accessible RAM location */
{
int found = 0;
for (desc = mmap ; !found && (desc < mmap_end) ;
desc = NEXT_MEMORY_DESCRIPTOR(desc, desc_size))
{
switch (desc->type)
{
case GRUB_EFI_CONVENTIONAL_MEMORY:
case GRUB_EFI_LOADER_CODE:
case GRUB_EFI_LOADER_DATA:
min_start = desc->physical_start + min_offset;
found = 1;
break;
default:
break;
}
}
}
/* First, find free pages for the real mode code
and the memory map buffer. */
for (desc = mmap ; desc < mmap_end ;
desc = NEXT_MEMORY_DESCRIPTOR(desc, desc_size))
{
grub_uint64_t start, end;
grub_dprintf("mm", "%s: 0x%08x bytes @ 0x%08x\n",
__FUNCTION__,
(grub_uint32_t) (desc->num_pages << PAGE_SHIFT),
(grub_uint32_t) (desc->physical_start));
if (desc->type != GRUB_EFI_CONVENTIONAL_MEMORY)
continue;
start = desc->physical_start;
end = start + (desc->num_pages << PAGE_SHIFT);
grub_dprintf("mm", "%s: start=0x%016llx, end=0x%016llx\n",
__FUNCTION__, start, end);
start = start < min_start ? min_start : start;
if (start + size > end)
continue;
grub_dprintf("mm", "%s: let's allocate some (0x%x) pages @ 0x%08x...\n",
__FUNCTION__, (size >> PAGE_SHIFT), (grub_addr_t) start);
mem = grub_efi_allocate_pages (start, (size >> PAGE_SHIFT) + 1);
grub_dprintf("mm", "%s: retval=0x%08x\n",
__FUNCTION__, (grub_addr_t) mem);
if (! mem)
{
grub_error (GRUB_ERR_OUT_OF_MEMORY, "cannot allocate memory");
goto fail;
}
break;
}
if (! mem)
{
grub_error (GRUB_ERR_OUT_OF_MEMORY, "cannot allocate memory");
goto fail;
}
grub_free (mmap);
return mem;
fail:
grub_free (mmap);
return NULL;
}
grub_err_t
grub_efi_prepare_platform (void)
{
grub_efi_uintn_t mmap_size;
grub_efi_uintn_t map_key;
grub_efi_uintn_t desc_size;
grub_efi_uint32_t desc_version;
grub_efi_memory_descriptor_t *mmap_buf;
grub_err_t err;
/*
* Cloned from IA64
* Must be done after grub_machine_fini because map_key is used by
*exit_boot_services.
*/
mmap_size = find_mmap_size ();
if (! mmap_size)
return GRUB_ERR_OUT_OF_MEMORY;
mmap_buf = grub_efi_allocate_pages (0, page_align (mmap_size) >> 12);
if (! mmap_buf)
return GRUB_ERR_OUT_OF_MEMORY;
err = grub_efi_finish_boot_services (&mmap_size, mmap_buf, &map_key,
&desc_size, &desc_version);
if (err != GRUB_ERR_NONE)
return err;
grub_arm_disable_caches_mmu();
return GRUB_ERR_NONE;
}

View file

@ -0,0 +1,38 @@
/*
* (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
.thumb @ For relocation debugging
blx _start
.end

44
grub-core/kern/arm/misc.S Normal file
View file

@ -0,0 +1,44 @@
/*
* 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
#define ARM(x...) x
#define THUMB(x...)
#else
.thumb
#define THUMB(x...) x
#define ARM(x...)
#endif
.align 2
/*
* Null divide-by-zero handler
*/
FUNCTION(raise)
mov r0, #0
bx lr
.end

View file

@ -0,0 +1,176 @@
/*
* 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.
*
*
* 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).
* <overflow>:
* Any part of the module blob that extends beyond _end.
* <modules>:
* Loadable modules, post relocation.
* <stack>:
* <heap>:
*/
.text
.arm
FUNCTION(_start)
b codestart
@ Size of final image integrated module blob - set by grub-mkimage
. = _start + GRUB_KERNEL_MACHINE_TOTAL_MODULE_SIZE
VARIABLE(grub_total_module_size)
.long 0
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
ldr sp, =entry_state
push {r4-r12,lr} @ store U-Boot context (sp in r12)
@ Put kernel parameters aside until we can store them (further down)
mov r4, r1 @ machine type
mov r5, r2 @ boot data
@ Modules have been stored as a blob in BSS,
@ they need to be manually relocated to _end or
@ (__bss_start + grub_total_module_size), whichever greater.
bl uboot_get_real_bss_start @ r0 = src
ldr r1, =EXT_C(_end) @ dst = End of BSS
ldr r2, grub_total_module_size @ blob size
add r3, r0, r2 @ blob end
cmp r1, r3 @ _end < blob end?
movlt r1, r3 @ dst = blob end + blob size
1: ldr r3, [r0], #4 @ r3 = *src++
str r3, [r1], #4 @ *dst++ = r3
subs r2, #4 @ remaining -= 4
bne 1b @ while remaining != 0
@ Set up a new stack, beyond the end of copied modules.
ldr r3, =GRUB_KERNEL_MACHINE_STACK_SIZE
add r3, r1, r3 @ Place stack beyond end of modules
and sp, r3, #~0x7 @ Ensure 8-byte alignment
@ Since we _are_ the C run-time, we need to manually zero the BSS
@ region before continuing
bl uboot_get_real_bss_start @ zero from here
ldr r1, =EXT_C(_end) @ to here
mov r2, #0
1: str r2, [r0], #4
cmp r0, r1
bne 1b
@ Global variables now accessible - store kernel parameters in memory
ldr r12, =EXT_C(uboot_machine_type)
str r4, [r12]
ldr r12, =EXT_C(uboot_boot_data)
str r5, [r12]
b EXT_C(grub_main)
/*
* __bss_start does not actually point to the start of the runtime
* BSS, but rather to the next byte following the preceding data.
*/
FUNCTION (uboot_get_real_bss_start)
ldr r0, =EXT_C(__bss_start) @ src
tst r0, #(GRUB_KERNEL_MACHINE_MOD_ALIGN - 1)
beq 1f
mvn r1, #(GRUB_KERNEL_MACHINE_MOD_ALIGN - 1)
and r0, r0, r1
add r0, r0, #(GRUB_KERNEL_MACHINE_MOD_ALIGN)
1: bx lr
/*
* 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(uboot_syscall)
ldr ip, =transition_space
stm ip, {r8, lr}
ldr ip, =gd_backup
ldr r8, [ip]
ldr ip, =uboot_syscall_ptr
mov lr, pc
ldr pc, [ip]
ldr ip, =gd_backup
str r8, [ip]
ldr ip, =transition_space
ldm ip, {r8, lr}
bx lr
FUNCTION(uboot_return)
ldr sp, =entry_state_end
pop {r4-r12, lr}
mov sp, r12
bx lr
.data
.align 3 @ 8-byte alignment for stack
@ U-boot context stack space
entry_state_end:
.long 0 @ r4
.long 0 @ r5
.long 0 @ r6
.long 0 @ r7
gd_backup:
.long 0 @ r8 - U-Boot global data pointer
.long 0 @ r9
.long 0 @ r10
.long 0 @ r11
VARIABLE(uboot_search_hint)@ U-Boot stack pointer -
.long 0 @ also API signature address hint.
.long 0 @ lr
entry_state: @ backup for U-Boot context
@ GRUB context stack space
transition_space:
.long 0 @ r8
.long 0 @ lr
VARIABLE(uboot_syscall_ptr)
.long 0 @
.end

112
grub-core/kern/uboot/hw.c Normal file
View file

@ -0,0 +1,112 @@
/* hw.c - U-Boot hardware discovery */
/*
* 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/kernel.h>
#include <grub/memory.h>
#include <grub/misc.h>
#include <grub/mm.h>
#include <grub/offsets.h>
#include <grub/machine/kernel.h>
#include <grub/uboot/disk.h>
#include <grub/uboot/uboot.h>
grub_addr_t start_of_ram;
/*
* grub_uboot_probe_memory():
* Queries U-Boot for available memory regions.
*
* Sets up heap near the image in memory and sets up "start_of_ram".
*/
void
grub_uboot_mm_init (void)
{
struct sys_info *si = uboot_get_sys_info ();
grub_mm_init_region ((void *) (grub_modules_get_end ()
+ GRUB_KERNEL_MACHINE_STACK_SIZE),
GRUB_KERNEL_MACHINE_HEAP_SIZE);
if (si && (si->mr_no != 0))
{
int i;
start_of_ram = GRUB_UINT_MAX;
for (i = 0; i < si->mr_no; i++)
if ((si->mr[i].flags & MR_ATTR_MASK) == MR_ATTR_DRAM)
if (si->mr[i].start < start_of_ram)
start_of_ram = si->mr[i].start;
}
}
/*
* grub_uboot_probe_hardware():
*
*/
grub_err_t
grub_uboot_probe_hardware (void)
{
int devcount, i;
devcount = uboot_dev_enum ();
grub_dprintf ("init", "%d devices found\n", devcount);
for (i = 0; i < devcount; i++)
{
struct device_info *devinfo = uboot_dev_get (i);
grub_dprintf ("init", "device handle: %d\n", i);
grub_dprintf ("init", " cookie\t= 0x%08x\n",
(grub_uint32_t) devinfo->cookie);
if (devinfo->type & DEV_TYP_STOR)
{
grub_dprintf ("init", " type\t\t= DISK\n");
grub_ubootdisk_register (devinfo, i);
}
else if (devinfo->type & DEV_TYP_NET)
{
grub_dprintf ("init", " type\t\t= NET (not supported yet)\n");
}
else
{
grub_dprintf ("init", "%s: unknown device type", __FUNCTION__);
}
}
return GRUB_ERR_NONE;
}
grub_err_t
grub_machine_mmap_iterate (grub_memory_hook_t hook, void *hook_data)
{
int i;
struct sys_info *si = uboot_get_sys_info ();
if (!si || (si->mr_no < 1))
return GRUB_ERR_BUG;
/* Iterate and call `hook'. */
for (i = 0; i < si->mr_no; i++)
if ((si->mr[i].flags & MR_ATTR_MASK) == MR_ATTR_DRAM)
hook (si->mr[i].start, si->mr[i].size, GRUB_MEMORY_AVAILABLE,
hook_data);
return GRUB_ERR_NONE;
}

171
grub-core/kern/uboot/init.c Normal file
View file

@ -0,0 +1,171 @@
/* init.c - generic U-Boot initialization and finalization */
/*
* 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/offsets.h>
#include <grub/term.h>
#include <grub/time.h>
#include <grub/machine/kernel.h>
#include <grub/uboot/console.h>
#include <grub/uboot/disk.h>
#include <grub/uboot/uboot.h>
extern char __bss_start[];
extern char _end[];
extern grub_size_t grub_total_module_size;
extern int (*uboot_syscall_ptr) (int, int *, ...);
grub_addr_t grub_modbase;
grub_uint32_t uboot_machine_type;
grub_addr_t uboot_boot_data;
static unsigned long timer_start;
void
grub_exit (void)
{
uboot_return (0);
}
grub_uint32_t
uboot_get_machine_type (void)
{
return uboot_machine_type;
}
grub_addr_t
uboot_get_boot_data (void)
{
return uboot_boot_data;
}
static grub_uint64_t
uboot_timer_ms (void)
{
return (grub_uint64_t) uboot_get_timer (timer_start);
}
void
grub_machine_init (void)
{
grub_addr_t end, real_bss_start;
int ver;
/* First of all - establish connection with U-Boot */
ver = uboot_api_init ();
if (!ver)
{
/* Don't even have a console to log errors to... */
grub_exit ();
}
else if (ver > API_SIG_VERSION)
{
/* Try to print an error message */
uboot_puts ("invalid U-Boot API version\n");
}
/*
* Modules were relocated to _end, or __bss_start + grub_total_module_size,
* whichever greater. (And __bss_start may not point to actual BSS start...)
*/
real_bss_start = uboot_get_real_bss_start ();
end = real_bss_start + grub_total_module_size;
if (end < (grub_addr_t) _end)
end = (grub_addr_t) _end;
grub_modbase = end;
/* Initialize the console so that GRUB can display messages. */
grub_console_init_early ();
/* Enumerate memory and initialize the memory management system. */
grub_uboot_mm_init ();
grub_dprintf ("init", "__bss_start: 0x%08x, real_bss_start: 0x%08x\n",
(grub_addr_t) __bss_start, real_bss_start);
grub_dprintf ("init", "end: 0x%08x, _end: 0x%08x\n",
(grub_addr_t) end, (grub_addr_t) _end);
grub_dprintf ("init", "grub_modbase: %p\n", (void *) grub_modbase);
grub_dprintf ("init", "grub_modules_get_end(): %p\n",
(void *) grub_modules_get_end ());
/* Initialise full terminfo support */
grub_console_init_lately ();
/* Enumerate uboot devices */
grub_uboot_probe_hardware ();
/* Initialise timer */
timer_start = uboot_get_timer (0);
grub_install_get_time_ms (uboot_timer_ms);
/* Initialize */
grub_ubootdisk_init ();
}
void
grub_machine_fini (void)
{
}
/*
* grub_machine_get_bootlocation():
* Called from kern/main.c, which expects a device name (minus parentheses)
* and a filesystem path back, if any are known.
* Any returned values must be pointers to dynamically allocated strings.
*/
void
grub_machine_get_bootlocation (char **device, char **path)
{
char *tmp;
tmp = uboot_env_get ("grub_bootdev");
if (tmp)
{
*device = grub_malloc (grub_strlen (tmp) + 1);
if (*device == NULL)
return;
grub_strncpy (*device, tmp, grub_strlen (tmp) + 1);
}
else
*device = NULL;
tmp = uboot_env_get ("grub_bootpath");
if (tmp)
{
*path = grub_malloc (grub_strlen (tmp) + 1);
if (*path == NULL)
return;
grub_strncpy (*path, tmp, grub_strlen (tmp) + 1);
}
else
*path = NULL;
}
void
grub_uboot_fini (void)
{
grub_ubootdisk_fini ();
grub_console_fini ();
}

View file

@ -0,0 +1,363 @@
/*
* 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/misc.h>
#include <grub/mm.h>
#include <grub/uboot/uboot.h>
/*
* The main syscall entry point is not reentrant, only one call is
* serviced until finished.
*
* int syscall(int call, int *retval, ...)
* e.g. syscall(1, int *, u_int32_t, u_int32_t, u_int32_t, u_int32_t);
*
* call: syscall number
*
* retval: points to the return value placeholder, this is the place the
* syscall puts its return value, if NULL the caller does not
* expect a return value
*
* ... syscall arguments (variable number)
*
* returns: 0 if the call not found, 1 if serviced
*/
extern int (*uboot_syscall_ptr) (int, int *, ...);
extern int uboot_syscall (int, int *, ...);
extern grub_addr_t uboot_search_hint;
static struct sys_info uboot_sys_info;
static struct mem_region uboot_mem_info[5];
static struct device_info uboot_devices[6];
static int num_devices;
int
uboot_api_init (void)
{
struct api_signature *start, *end;
struct api_signature *p;
if (uboot_search_hint)
{
/* Extended search range to work around Trim Slice U-Boot issue */
start = (struct api_signature *) ((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)
{
uboot_syscall_ptr = p->syscall;
return p->version;
}
}
return 0;
}
/* All functions below are wrappers around the uboot_syscall() function */
/*
* int API_getc(int *c)
*/
int
uboot_getc (void)
{
int c;
if (!uboot_syscall (API_GETC, NULL, &c))
return -1;
return c;
}
/*
* int API_tstc(int *c)
*/
int
uboot_tstc (void)
{
int c;
if (!uboot_syscall (API_TSTC, NULL, &c))
return -1;
return c;
}
/*
* int API_putc(char *ch)
*/
void
uboot_putc (int c)
{
uboot_syscall (API_PUTC, NULL, &c);
}
/*
* int API_puts(const char *s)
*/
void
uboot_puts (const char *s)
{
uboot_syscall (API_PUTS, NULL, s);
}
/*
* int API_reset(void)
*/
void
uboot_reset (void)
{
uboot_syscall (API_RESET, NULL, 0);
}
/*
* int API_get_sys_info(struct sys_info *si)
*
* fill out the sys_info struct containing selected parameters about the
* machine
*/
struct sys_info *
uboot_get_sys_info (void)
{
int retval;
grub_memset (&uboot_sys_info, 0, sizeof (uboot_sys_info));
grub_memset (&uboot_mem_info, 0, sizeof (uboot_mem_info));
uboot_sys_info.mr = uboot_mem_info;
uboot_sys_info.mr_no = sizeof (uboot_mem_info) / sizeof (struct mem_region);
if (uboot_syscall (API_GET_SYS_INFO, &retval, &uboot_sys_info))
if (retval == 0)
return &uboot_sys_info;
return NULL;
}
/*
* int API_udelay(unsigned long *udelay)
*/
void
uboot_udelay (grub_uint32_t usec)
{
uboot_syscall (API_UDELAY, NULL, &usec);
}
/*
* int API_get_timer(unsigned long *current, unsigned long *base)
*/
grub_uint32_t
uboot_get_timer (grub_uint32_t base)
{
grub_uint32_t current;
if (!uboot_syscall (API_GET_TIMER, NULL, &current, &base))
return 0;
return current;
}
/*
* int API_dev_enum(struct device_info *)
*
*/
int
uboot_dev_enum (void)
{
int max;
grub_memset (&uboot_devices, 0, sizeof (uboot_devices));
max = sizeof (uboot_devices) / sizeof (struct device_info);
/*
* The API_DEV_ENUM call starts a fresh enumeration when passed a
* struct device_info with a NULL cookie, and then depends on having
* the prevoiusly enumerated device cookie "seeded" into the target
* structure.
*/
if (!uboot_syscall (API_DEV_ENUM, NULL, &uboot_devices)
|| uboot_devices[0].cookie == NULL)
return 0;
for (num_devices = 1; num_devices < max; num_devices++)
{
uboot_devices[num_devices].cookie =
uboot_devices[num_devices - 1].cookie;
if (!uboot_syscall (API_DEV_ENUM, NULL, &uboot_devices[num_devices]))
return 0;
/* When no more devices to enumerate, target cookie set to NULL */
if (uboot_devices[num_devices].cookie == NULL)
break;
}
return num_devices;
}
#define VALID_DEV(x) (((x) < num_devices) && ((x) >= 0))
#define OPEN_DEV(x) (VALID_DEV(x) && (uboot_devices[(x)].state == DEV_STA_OPEN))
struct device_info *
uboot_dev_get (int handle)
{
if (VALID_DEV (handle))
return &uboot_devices[handle];
return NULL;
}
/*
* int API_dev_open(struct device_info *)
*/
int
uboot_dev_open (int handle)
{
struct device_info *dev;
int retval;
if (!VALID_DEV (handle))
return -1;
dev = &uboot_devices[handle];
if (!uboot_syscall (API_DEV_OPEN, &retval, dev))
return -1;
return retval;
}
/*
* int API_dev_close(struct device_info *)
*/
int
uboot_dev_close (int handle)
{
struct device_info *dev;
int retval;
if (!VALID_DEV (handle))
return -1;
dev = &uboot_devices[handle];
if (!uboot_syscall (API_DEV_CLOSE, &retval, dev))
return -1;
return retval;
}
/*
* int API_dev_read(struct device_info *di, void *buf, size_t *len,
* unsigned long *start, size_t *act_len)
*/
int
uboot_dev_read (int handle, void *buf, lbasize_t blocks,
lbastart_t start, lbasize_t * real_blocks)
{
struct device_info *dev;
int retval;
if (!OPEN_DEV (handle))
return -1;
dev = &uboot_devices[handle];
if (!uboot_syscall (API_DEV_READ, &retval, dev, buf,
&blocks, &start, real_blocks))
return -1;
return retval;
}
/*
* int API_dev_read(struct device_info *di, void *buf,
* size_t *len, size_t *act_len)
*/
int
uboot_dev_recv (int handle, void *buf, int size, int *real_size)
{
struct device_info *dev;
int retval;
if (!OPEN_DEV (handle))
return -1;
dev = &uboot_devices[handle];
if (!uboot_syscall (API_DEV_READ, &retval, dev, buf, &size, real_size))
return -1;
return retval;
}
/*
* Notice: this is for sending network packets only, as U-Boot does not
* support writing to storage at the moment (12.2007)
*
* int API_dev_write(struct device_info *di, void *buf, int *len)
*/
int
uboot_dev_send (int handle, void *buf, int size)
{
struct device_info *dev;
int retval;
if (!OPEN_DEV (handle))
return -1;
dev = &uboot_devices[handle];
if (!uboot_syscall (API_DEV_WRITE, &retval, dev, buf, &size))
return -1;
return retval;
}
/*
* int API_env_get(const char *name, char **value)
*/
char *
uboot_env_get (const char *name)
{
char *value;
if (!uboot_syscall (API_ENV_GET, NULL, name, &value))
return NULL;
return value;
}
/*
* int API_env_set(const char *name, const char *value)
*/
void
uboot_env_set (const char *name, const char *value)
{
uboot_syscall (API_ENV_SET, NULL, name, value);
}