721 lines
17 KiB
C
721 lines
17 KiB
C
#define DEBUG_BOOT_MODE 0
|
|
#include <linux/err.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/types.h>
|
|
#include <asm/pic.h>
|
|
#include <asm/bootinfo.h>
|
|
#include <asm/cpu_regs.h>
|
|
#include <asm/e2k_api.h>
|
|
#include <asm/head.h>
|
|
#include <asm/string.h>
|
|
#include <asm/mpspec.h>
|
|
#include <asm/kvm/hypercall.h>
|
|
|
|
#define BOOT_HEAP_SIZE 0x1000000
|
|
static unsigned long free_mem_ptr;
|
|
static unsigned long free_mem_end_ptr;
|
|
|
|
#define STATIC static
|
|
|
|
static void error_loop(char *s);
|
|
#define assert(condition) \
|
|
do { \
|
|
if (unlikely(!(condition))) \
|
|
error_loop("Assertion failed: " #condition); \
|
|
} while (0)
|
|
|
|
#ifdef CONFIG_KERNEL_GZIP
|
|
#include "../../../../lib/decompress_inflate.c"
|
|
#endif
|
|
|
|
#ifdef CONFIG_KERNEL_BZIP2
|
|
#include "../../../../lib/decompress_bunzip2.c"
|
|
#endif
|
|
|
|
#ifdef CONFIG_KERNEL_LZ4
|
|
#include "../../../../lib/decompress_unlz4.c"
|
|
#endif
|
|
|
|
#ifdef CONFIG_KERNEL_XZ
|
|
#define memmove memmove
|
|
#include "../../../../lib/decompress_unxz.c"
|
|
#endif
|
|
|
|
#ifdef CONFIG_KERNEL_LZMA
|
|
#include "../../../../lib/decompress_unlzma.c"
|
|
#endif
|
|
|
|
#ifdef CONFIG_KERNEL_LZO
|
|
#include "../../../../lib/decompress_unlzo.c"
|
|
#endif
|
|
|
|
/* Symbols defined by linker scripts */
|
|
extern char _bss[], _ebss[];
|
|
extern char _got[], _egot[];
|
|
extern char _kernel[], _ekernel[];
|
|
extern char _start[], _end[];
|
|
extern char __orig_kernel_size[];
|
|
|
|
struct mem_bank {
|
|
unsigned long mb_bottom;
|
|
unsigned long mb_top;
|
|
};
|
|
|
|
/* Add some number to account for banks being broken by reserved memory */
|
|
#define MAX_MEM_BANKS (L_MAX_MEM_NUMNODES * L_MAX_NODE_PHYS_BANKS + 64)
|
|
struct board_mem {
|
|
unsigned long bm_size;
|
|
unsigned bm_nBanks;
|
|
struct mem_bank bm_Banks[MAX_MEM_BANKS];
|
|
};
|
|
|
|
/*
|
|
* Put 'got_updating_in_progress' and 'unpacking_in_progress' into
|
|
* compiler-initialized .data section so that all processors can access it
|
|
* before .bss section is cleared.
|
|
*/
|
|
static int got_updating_in_progress = 1;
|
|
static int unpacking_in_progress = 1;
|
|
|
|
static boot_info_t *boot_info;
|
|
|
|
static unsigned long kernel_address;
|
|
|
|
static unsigned long io_area_phys_base;
|
|
|
|
#ifdef CONFIG_KVM_GUEST_KERNEL
|
|
#define STARTUP_TTABLE_ENTRY_OFFSET 0x10000
|
|
|
|
static unsigned long
|
|
dec_guest_mmio(unsigned long addr, u64 value, u8 size, u8 is_write)
|
|
{
|
|
unsigned long data[1];
|
|
|
|
if (is_write)
|
|
data[0] = value;
|
|
|
|
assert(!HYPERVISOR_guest_mmio_request(addr, data, size, is_write));
|
|
|
|
return data[0];
|
|
}
|
|
|
|
static void dec_writeb(u8 b, void __iomem *addr)
|
|
{
|
|
dec_guest_mmio((unsigned long) addr, b, 1, 1);
|
|
}
|
|
|
|
static u8 dec_readb(void __iomem *addr)
|
|
{
|
|
return dec_guest_mmio((unsigned long) addr, 0, 1, 0);
|
|
}
|
|
|
|
static u32 dec_readl(void __iomem *addr)
|
|
{
|
|
return dec_guest_mmio((unsigned long) addr, 0, 4, 0);
|
|
}
|
|
#else
|
|
#define STARTUP_TTABLE_ENTRY_OFFSET 0x6000
|
|
|
|
static void dec_writeb(u8 b, void __iomem *addr)
|
|
{
|
|
NATIVE_WRITE_MAS_B((unsigned long) addr, b, MAS_IOADDR);
|
|
}
|
|
|
|
static u8 dec_readb(void __iomem *addr)
|
|
{
|
|
return NATIVE_READ_MAS_B((unsigned long) addr, MAS_IOADDR);
|
|
}
|
|
|
|
static u32 dec_readl(void __iomem *addr)
|
|
{
|
|
return NATIVE_READ_MAS_W((unsigned long) addr, MAS_IOADDR);
|
|
}
|
|
#endif
|
|
|
|
static inline u8 am85c30_com_inb_command(u64 iomem_addr, u8 reg_num)
|
|
{
|
|
dec_writeb(reg_num, (void __iomem *) iomem_addr);
|
|
return dec_readb((void __iomem *) iomem_addr);
|
|
}
|
|
|
|
static inline void am85c30_com_outb(u64 iomem_addr, u8 byte)
|
|
{
|
|
dec_writeb(byte, (void __iomem *) iomem_addr);
|
|
}
|
|
|
|
static inline unsigned int dec_epic_is_bsp(void)
|
|
{
|
|
union cepic_ctrl reg;
|
|
|
|
reg.raw = dec_readl((void __iomem *)(EPIC_DEFAULT_PHYS_BASE + CEPIC_CTRL));
|
|
return reg.bits.bsp_core;
|
|
}
|
|
|
|
static inline unsigned int dec_apic_is_bsp(void)
|
|
{
|
|
return BootStrap(dec_readl((void __iomem *)(APIC_DEFAULT_PHYS_BASE + APIC_BSP)));
|
|
}
|
|
|
|
#define AM85C30_RR0 0x00
|
|
#define AM85C30_D2 (0x01 << 2)
|
|
static void am85c30_putc(unsigned long port, char c)
|
|
{
|
|
/*
|
|
* Output to ttyS0
|
|
*/
|
|
while ((am85c30_com_inb_command(port, AM85C30_RR0) & AM85C30_D2) == 0)
|
|
E2K_NOP(7);
|
|
am85c30_com_outb(port + 0x01, c);
|
|
|
|
/*
|
|
* Output to ttyS1
|
|
*/
|
|
port += 2;
|
|
while ((am85c30_com_inb_command(port, AM85C30_RR0) & AM85C30_D2) == 0)
|
|
E2K_NOP(7);
|
|
am85c30_com_outb(port + 0x01, c);
|
|
}
|
|
|
|
static void __putc(unsigned long port, char c)
|
|
{
|
|
am85c30_putc(port, c);
|
|
}
|
|
|
|
static void putc(char c)
|
|
{
|
|
unsigned long port = boot_info->serial_base;
|
|
|
|
if (!port)
|
|
return;
|
|
|
|
__putc(port, c);
|
|
if (c == '\n')
|
|
__putc(port, '\r');
|
|
}
|
|
|
|
static void puts(char *s)
|
|
{
|
|
while (*s)
|
|
putc(*s++);
|
|
}
|
|
|
|
/*
|
|
* Use global variables to prevent using data stack
|
|
*/
|
|
static const char hex_numbers_for_debug[16] = {
|
|
'0', '1', '2', '3', '4', '5', '6', '7',
|
|
'8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
|
|
};
|
|
|
|
static void put_u64(u64 num, int newline)
|
|
{
|
|
char u64_char[18];
|
|
int i;
|
|
|
|
if (newline) {
|
|
u64_char[16] = '\n';
|
|
u64_char[17] = 0;
|
|
} else {
|
|
u64_char[16] = 0;
|
|
}
|
|
|
|
for (i = 0; i < 16; i++) {
|
|
u64_char[15 - i] = hex_numbers_for_debug[num % 16];
|
|
num = num / 16;
|
|
}
|
|
puts(u64_char);
|
|
}
|
|
|
|
|
|
static void error(char *str)
|
|
{
|
|
puts(str);
|
|
putc('\n');
|
|
}
|
|
|
|
static void error_loop(char *s)
|
|
{
|
|
puts(s);
|
|
for (;;)
|
|
E2K_NOP(7);
|
|
}
|
|
|
|
static void probe_node_memory(bootblock_struct_t *bootblock, int node,
|
|
bank_info_t *bank_info, bank_info_t **bank_info_ex_p,
|
|
struct board_mem *bm)
|
|
{
|
|
boot_info_t *bootinfo = &bootblock->info;
|
|
bank_info_t *bank_info_ex = *bank_info_ex_p;
|
|
int bank, bm_bank;
|
|
|
|
for (bank = 0; bank < L_MAX_NODE_PHYS_BANKS; bank++) {
|
|
unsigned long bank_start, bank_end;
|
|
|
|
if (bank >= L_MAX_NODE_PHYS_BANKS_FUSTY) {
|
|
int banks_ex_id = bank_info - bootinfo->bios.banks_ex;
|
|
|
|
if (bank == L_MAX_NODE_PHYS_BANKS_FUSTY) {
|
|
bank_info = bank_info_ex;
|
|
banks_ex_id = bank_info -
|
|
bootinfo->bios.banks_ex;
|
|
}
|
|
if (banks_ex_id >= L_MAX_PHYS_BANKS_EX) {
|
|
bank_info_ex = bank_info;
|
|
puts("WARNING: Node has phys banks in extended area, but extended area is full, ignored\n");
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
if (bank_info->size == 0) {
|
|
if (bank >= L_MAX_NODE_PHYS_BANKS_FUSTY)
|
|
bank_info_ex = bank_info + 1;
|
|
goto out; /* no more banks on node */
|
|
}
|
|
|
|
bank_start = bank_info->address;
|
|
bank_end = bank_start + bank_info->size;
|
|
|
|
#ifdef DEBUG
|
|
puts("Memory bank from 0x");
|
|
put_u64(bank_start, false);
|
|
puts(" to 0x");
|
|
put_u64(bank_end, true);
|
|
#endif
|
|
|
|
bm_bank = bm->bm_nBanks;
|
|
if (bm_bank > 0 &&
|
|
bm->bm_Banks[bm_bank - 1].mb_top == bank_start) {
|
|
/* Continue previous bank */
|
|
--bm_bank;
|
|
bm->bm_Banks[bm_bank].mb_top = bank_end;
|
|
} else {
|
|
/* Add new bank */
|
|
assert(bm_bank < MAX_MEM_BANKS);
|
|
bm->bm_Banks[bm_bank].mb_bottom = bank_start;
|
|
bm->bm_Banks[bm_bank].mb_top = bank_end;
|
|
++bm->bm_nBanks;
|
|
}
|
|
|
|
++bank_info;
|
|
}
|
|
|
|
if (bank == L_MAX_NODE_PHYS_BANKS) {
|
|
bank_info_ex = bank_info;
|
|
puts("WARNING: Node last phys bank for node in extended area is not null, ignored\n");
|
|
goto out;
|
|
}
|
|
|
|
if (bank < L_MAX_NODE_PHYS_BANKS_FUSTY) {
|
|
for (; bank < L_MAX_NODE_PHYS_BANKS_FUSTY; bank++) {
|
|
if (!bank_info++->size)
|
|
goto out;
|
|
}
|
|
} else {
|
|
bank_info_ex = bank_info;
|
|
}
|
|
|
|
while (bank_info_ex++->size) {
|
|
if (++bank >= L_MAX_NODE_PHYS_BANKS) {
|
|
puts("WARNING: Node last phys bank for node in extended area is not null, ignored\n");
|
|
break;
|
|
}
|
|
if (bank_info_ex - bootinfo->bios.banks_ex >=
|
|
L_MAX_PHYS_BANKS_EX) {
|
|
puts("WARNING: Node last phys bank in extended area is not null, ignored\n");
|
|
break;
|
|
}
|
|
}
|
|
|
|
out:
|
|
*bank_info_ex_p = bank_info_ex;
|
|
}
|
|
|
|
/*
|
|
* probe_memory - initialize free memory list
|
|
*/
|
|
static void probe_memory(bootblock_struct_t *bootblock, struct board_mem *bm)
|
|
{
|
|
boot_info_t *bootinfo = &bootblock->info;
|
|
bank_info_t *bank_info_ex = bootinfo->bios.banks_ex;
|
|
u_int64_t phys_nodes_map = bootinfo->nodes_map;
|
|
int node;
|
|
|
|
for (node = 0; node < L_MAX_MEM_NUMNODES; node++) {
|
|
bank_info_t *bank_info = bootinfo->nodes_mem[node].banks;
|
|
|
|
if (!(phys_nodes_map & (1UL << node)) || bank_info->size == 0)
|
|
continue;
|
|
|
|
probe_node_memory(bootblock, node, bank_info,
|
|
&bank_info_ex, bm);
|
|
}
|
|
}
|
|
|
|
static int intersect(struct mem_bank *b1, const struct mem_bank *b2,
|
|
struct mem_bank *b3, int ignore_busy)
|
|
{
|
|
assert(b1->mb_top >= b1->mb_bottom && b2->mb_top >= b2->mb_bottom);
|
|
|
|
if (b1->mb_bottom < b2->mb_bottom && b1->mb_top > b2->mb_top) {
|
|
/* Cut one bank into two */
|
|
b3->mb_bottom = b1->mb_bottom;
|
|
b3->mb_top = b2->mb_bottom;
|
|
b3++;
|
|
b3->mb_bottom = b2->mb_top;
|
|
b3->mb_top = b1->mb_top;
|
|
return 2;
|
|
}
|
|
|
|
if (b1->mb_bottom >= b2->mb_top || b1->mb_top <= b2->mb_bottom) {
|
|
/* No intersection */
|
|
*b3 = *b1;
|
|
} else {
|
|
/* Do not allow double reservations */
|
|
assert(ignore_busy || (b1->mb_bottom <= b2->mb_bottom &&
|
|
b1->mb_top >= b2->mb_top));
|
|
|
|
/* Intersection */
|
|
b3->mb_bottom = (b1->mb_bottom < b2->mb_bottom) ?
|
|
b1->mb_bottom : b2->mb_top;
|
|
b3->mb_top = (b1->mb_top > b2->mb_top) ? b1->mb_top :
|
|
b2->mb_bottom;
|
|
}
|
|
|
|
if (b3->mb_bottom < b3->mb_top)
|
|
return 1;
|
|
|
|
*b3 = (struct mem_bank) {0, 0};
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void sub(struct board_mem *from, const struct mem_bank *b,
|
|
struct board_mem *to, int ignore_busy)
|
|
{
|
|
int n, i;
|
|
|
|
for(i = 0, n = 0; i < from->bm_nBanks; i++)
|
|
n += intersect(&from->bm_Banks[i], b,
|
|
&to->bm_Banks[n], ignore_busy);
|
|
|
|
to->bm_nBanks = n;
|
|
}
|
|
|
|
static struct board_mem bm_tmp;
|
|
static void reserve_memory_area(struct board_mem *bm, unsigned long phys_addr,
|
|
unsigned long mem_size, int ignore_busy,
|
|
char *name)
|
|
{
|
|
struct mem_bank reserved;
|
|
unsigned long end_addr = phys_addr + mem_size;
|
|
|
|
assert(mem_size);
|
|
|
|
phys_addr = round_down(phys_addr, PAGE_SIZE);
|
|
end_addr = round_up(end_addr, PAGE_SIZE);
|
|
mem_size = end_addr - phys_addr;
|
|
|
|
#ifdef DEBUG
|
|
puts("Reserved ");
|
|
puts(name);
|
|
puts(" area: address 0x");
|
|
put_u64(phys_addr, false);
|
|
puts(", size 0x");
|
|
put_u64(mem_size, true);
|
|
#endif
|
|
|
|
reserved.mb_bottom = phys_addr;
|
|
reserved.mb_top = end_addr;
|
|
|
|
sub(bm, &reserved, &bm_tmp, ignore_busy);
|
|
|
|
/* Do not allow double reservations */
|
|
assert(ignore_busy || bm->bm_nBanks != bm_tmp.bm_nBanks ||
|
|
memcmp(bm->bm_Banks, &bm_tmp.bm_Banks, sizeof(*bm)));
|
|
|
|
memcpy(bm, &bm_tmp, sizeof(*bm));
|
|
}
|
|
|
|
/*
|
|
* Reserve the needed memory from MP - tables
|
|
*/
|
|
|
|
static void boot_reserve_mp_table(boot_info_t *bootinfo, struct board_mem *bm)
|
|
{
|
|
struct intel_mp_floating *mpf;
|
|
|
|
if (bootinfo->mp_table_base == 0UL)
|
|
return;
|
|
|
|
/*
|
|
* MP floating specification table
|
|
*/
|
|
reserve_memory_area(bm, bootinfo->mp_table_base, PAGE_SIZE,
|
|
1, "MP floating table");
|
|
|
|
mpf = (struct intel_mp_floating *) bootinfo->mp_table_base;
|
|
|
|
/*
|
|
* MP configuration table
|
|
*/
|
|
if (mpf->mpf_physptr != 0UL)
|
|
reserve_memory_area(bm, mpf->mpf_physptr, PAGE_SIZE,
|
|
1, "MP configuration table");
|
|
}
|
|
|
|
static void reserve_memory(boot_info_t *bootinfo, struct board_mem *bm)
|
|
{
|
|
unsigned long area_base, area_size;
|
|
psp_struct_t PSP = {{{0}}, {{0}}};
|
|
pcsp_struct_t PCSP = {{{0}}, {{0}}};
|
|
e2k_usbr_t USBR = {{0}};
|
|
usd_struct_t USD = {{{0}}, {{0}}};
|
|
int bank;
|
|
|
|
reserve_memory_area(bm, 0, PAGE_SIZE, 0, "0-page");
|
|
|
|
reserve_memory_area(bm, (unsigned long)_start,
|
|
(unsigned long) (_end - _start), 0, "kernel image");
|
|
|
|
reserve_memory_area(bm, 640 * 1024 /* ROM, VGA ... */,
|
|
(1024 - 640) * 1024, 0, "PC");
|
|
|
|
for (bank = 0; bank < bootinfo->num_of_busy; bank++) {
|
|
bank_info_t *busy_area = &bootinfo->busy[bank];
|
|
|
|
reserve_memory_area(bm, busy_area->address, busy_area->size,
|
|
1, "BIOS data");
|
|
}
|
|
|
|
if (boot_info->ramdisk_size)
|
|
reserve_memory_area(bm, boot_info->ramdisk_base,
|
|
boot_info->ramdisk_size, 1, "ramdisk");
|
|
|
|
reserve_memory_area(bm, 0x7ee00000, PAGE_SIZE, 1, "APIC page");
|
|
|
|
boot_reserve_mp_table(bootinfo, bm);
|
|
|
|
PSP = READ_PSP_REG();
|
|
reserve_memory_area(bm, PSP.PSP_base, PSP.PSP_size, 1,
|
|
"kernel boot-time procedures stack");
|
|
|
|
PCSP = READ_PCSP_REG();
|
|
reserve_memory_area(bm, PCSP.PCSP_base, PCSP.PCSP_size, 1,
|
|
"kernel boot-time procedure chain stack");
|
|
|
|
USBR = read_USBR_reg();
|
|
area_base = USBR.USBR_base;
|
|
read_USD_reg(&USD);
|
|
area_size = area_base - USD.USD_base + USD.USD_size;
|
|
area_base -= area_size;
|
|
reserve_memory_area(bm, area_base, area_size, 1,
|
|
"kernel boot-time data stack");
|
|
}
|
|
|
|
static unsigned long find_free_memory(struct board_mem *bm,
|
|
unsigned long size, unsigned long align)
|
|
{
|
|
unsigned long start, end;
|
|
struct mem_bank *bank;
|
|
int search_low = 0;
|
|
int i;
|
|
|
|
retry:
|
|
for (i = 0; i < bm->bm_nBanks; i++) {
|
|
bank = &bm->bm_Banks[i];
|
|
start = round_up(bank->mb_bottom, align);
|
|
end = round_down(bank->mb_top, align);
|
|
|
|
if (start < end && size <= end - start &&
|
|
(search_low || start >= 0x100000000UL)) {
|
|
reserve_memory_area(bm, start, size, 0, "allocated");
|
|
return start;
|
|
}
|
|
}
|
|
|
|
/* First try to find non-DMA memory */
|
|
if (!search_low) {
|
|
search_low = 1;
|
|
goto retry;
|
|
}
|
|
|
|
return -ENOMEM;
|
|
}
|
|
|
|
static __always_inline void jump_to_image(unsigned long kernel_address,
|
|
int n, bootblock_struct_t *bootblock)
|
|
{
|
|
e2k_oscud_lo_t oscud_lo;
|
|
|
|
/*
|
|
* Before jumping we must correct %oscud and %cud
|
|
* registers which contain kernel entry address.
|
|
*/
|
|
oscud_lo = READ_OSCUD_LO_REG();
|
|
AS(oscud_lo).base = kernel_address;
|
|
WRITE_OSCUD_LO_REG(oscud_lo);
|
|
WRITE_CUD_LO_REG(oscud_lo);
|
|
|
|
E2K_JUMP_ABSOLUTE_WITH_ARGUMENTS_2(kernel_address + STARTUP_TTABLE_ENTRY_OFFSET,
|
|
n, bootblock);
|
|
}
|
|
|
|
static struct board_mem memory;
|
|
extern int machdep_setup_features(int cpu, int revision);
|
|
|
|
/*
|
|
* Now we can use global variables (i.e. machine) and linker defined symbols (i.e. _bss)
|
|
*/
|
|
noinline void decompress_kernel_updated_got(int n, bootblock_struct_t *bootblock,
|
|
int bsp, e2k_idr_t idr, unsigned long orig_kernel_size)
|
|
{
|
|
struct board_mem *bm = &memory;
|
|
int ret;
|
|
|
|
if (!bsp) {
|
|
while (unpacking_in_progress)
|
|
E2K_NOP(7);
|
|
/* Barrier between reading `unpacking_in_progress'
|
|
* and reading unpacked kernel */
|
|
smp_rmb();
|
|
jump_to_image(kernel_address, n, bootblock);
|
|
}
|
|
|
|
/*
|
|
* Setup machine features
|
|
*/
|
|
assert(!machdep_setup_features(idr.IDR_mdl, idr.IDR_rev));
|
|
|
|
/*
|
|
* Clear .bss (guest variant uses machine)
|
|
*/
|
|
memset(_bss, 0, _ebss - _bss);
|
|
|
|
/*
|
|
* Initialize console and say hello
|
|
*/
|
|
boot_info = &bootblock->info;
|
|
|
|
if (read_IDR_reg().mdl == IDR_E1CP_MDL)
|
|
io_area_phys_base = E2K_LEGACY_SIC_IO_AREA_PHYS_BASE;
|
|
else
|
|
io_area_phys_base = E2K_FULL_SIC_IO_AREA_PHYS_BASE;
|
|
|
|
puts("\nDecompressor started\n");
|
|
|
|
#ifdef DEBUG
|
|
puts("Cleared .bss at 0x");
|
|
put_u64(_bss, false);
|
|
puts(", size 0x");
|
|
put_u64(_ebss - _bss, true);
|
|
#endif
|
|
|
|
/*
|
|
* Mark free and reserved memory
|
|
*/
|
|
probe_memory(bootblock, bm);
|
|
|
|
reserve_memory(&bootblock->info, bm);
|
|
|
|
/*
|
|
* Find free memory area for heap
|
|
*/
|
|
free_mem_ptr = find_free_memory(bm, BOOT_HEAP_SIZE + PAGE_SIZE, 8);
|
|
if (IS_ERR_VALUE(free_mem_ptr))
|
|
error_loop("ERROR: could not find free memory area for heap\n");
|
|
|
|
/* free_mem_ptr must not be equal to 0 */
|
|
if (!free_mem_ptr)
|
|
free_mem_ptr += PAGE_SIZE;
|
|
free_mem_end_ptr = free_mem_ptr + BOOT_HEAP_SIZE;
|
|
|
|
puts("Heap from 0x");
|
|
put_u64(free_mem_ptr, false);
|
|
puts(" to 0x");
|
|
put_u64(free_mem_end_ptr, true);
|
|
|
|
/*
|
|
* Decompress the kernel
|
|
*/
|
|
kernel_address = find_free_memory(bm,
|
|
orig_kernel_size, 0x400000);
|
|
if (IS_ERR_VALUE(kernel_address))
|
|
error_loop("ERROR: could not find free memory area to unpack kernel to\n");
|
|
|
|
puts("Unpacking 0x");
|
|
put_u64(orig_kernel_size, false);
|
|
puts(" bytes from 0x");
|
|
put_u64((unsigned long)_kernel, false);
|
|
puts(" to 0x");
|
|
put_u64(kernel_address, false);
|
|
puts("...\n");
|
|
|
|
ret = __decompress(_kernel, _ekernel - _kernel, NULL,
|
|
NULL, (char *) kernel_address, 0, NULL, error);
|
|
if (ret)
|
|
error_loop("ERROR: failed to unpack kernel\n");
|
|
|
|
puts("Done\n");
|
|
|
|
/*
|
|
* Tell others they can proceed
|
|
*/
|
|
bootblock->info.kernel_base = kernel_address;
|
|
bootblock->info.kernel_size = orig_kernel_size;
|
|
smp_wmb(); /* Wait for unpacked kernel and bootblock changes */
|
|
unpacking_in_progress = 0;
|
|
|
|
/*
|
|
* Jump to the kernel
|
|
*/
|
|
jump_to_image(kernel_address, n, bootblock);
|
|
}
|
|
|
|
/*
|
|
* Updating GOT should be done in a separate function. Otherwise compiler might put
|
|
* GOT load before GOT update (even ignoring the memory clobbers).
|
|
* Using global variables isn't allowed here.
|
|
*/
|
|
__section(.boot_entry)
|
|
void decompress_kernel(int n, bootblock_struct_t *bootblock)
|
|
{
|
|
unsigned long load_offset, got, egot, addr;
|
|
unsigned long orig_kernel_size = 0;
|
|
e2k_idr_t idr;
|
|
int bsp;
|
|
|
|
/*
|
|
* Only bootstrap processor proceeds to unpacking
|
|
*/
|
|
idr = read_IDR_reg();
|
|
|
|
if (idr.mdl >= IDR_E12C_MDL)
|
|
bsp = dec_epic_is_bsp();
|
|
else
|
|
bsp = dec_apic_is_bsp();
|
|
|
|
if (!bsp) {
|
|
while (got_updating_in_progress)
|
|
E2K_NOP(7);
|
|
/* Barrier between reading `got_updating_in_progress'
|
|
* and reading GOT */
|
|
smp_rmb();
|
|
} else {
|
|
load_offset = AS(READ_OSCUD_LO_REG()).base - 0x10000;
|
|
got = (unsigned long)_got + load_offset;
|
|
egot = (unsigned long)_egot + load_offset;
|
|
|
|
/* orig_kernel_size should not be shifted by load_offset */
|
|
orig_kernel_size = (unsigned long)__orig_kernel_size;
|
|
|
|
/* Update GOT */
|
|
for (addr = got; addr < egot; addr += 8)
|
|
*((unsigned long *)addr) += load_offset;
|
|
|
|
smp_wmb(); /* Wait for GOT changes */
|
|
got_updating_in_progress = 0;
|
|
}
|
|
|
|
decompress_kernel_updated_got(n, bootblock, bsp, idr, orig_kernel_size);
|
|
}
|