diff -Nraup a/kexec/arch/ia64/kexec-elf-ia64.c b/kexec/arch/ia64/kexec-elf-ia64.c --- a/kexec/arch/ia64/kexec-elf-ia64.c 2004-12-22 04:01:37.000000000 +0800 +++ b/kexec/arch/ia64/kexec-elf-ia64.c 2005-11-09 03:39:16.000000000 +0800 @@ -6,6 +6,7 @@ * Copyright (C) 2004 Silicon Graphics, Inc. * Jesse Barnes * Copyright (C) 2004 Khalid Aziz Hewlett Packard Co + * Copyright (C) 2005 Zou Nan hai Intel Corp * * 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 @@ -34,6 +35,7 @@ #include #include #include +#include #include #include #include @@ -74,23 +76,29 @@ void elf_ia64_usage(void) { printf( " --command-line=STRING Set the kernel command line to STRING.\n" - " --append=STRING Set the kernel command line to STRING.\n"); + " --append=STRING Set the kernel command line to STRING.\n" + " --initrd=STRING Set the kernel initrd to STRING.\n"); } int elf_ia64_load(int argc, char **argv, const char *buf, off_t len, struct kexec_info *info) { struct mem_ehdr ehdr; - const char *command_line; - int command_line_len; - unsigned long entry, max_addr; + const char *command_line, *ramdisk=0; + char *ramdisk_buf; + off_t ramdisk_size = 0; + unsigned long command_line_len; + unsigned long entry, max_addr, gp_value; + unsigned command_line_base, ramdisk_base; int result; int opt; #define OPT_APPEND (OPT_ARCH_MAX+0) +#define OPT_RAMDISK (OPT_ARCH_MAX+1) static const struct option options[] = { KEXEC_ARCH_OPTIONS {"command-line", 1, 0, OPT_APPEND}, {"append", 1, 0, OPT_APPEND}, + {"initrd", 1, 0, OPT_RAMDISK}, {0, 0, 0, 0}, }; @@ -110,11 +118,14 @@ int elf_ia64_load(int argc, char **argv, case OPT_APPEND: command_line = optarg; break; + case OPT_RAMDISK: + ramdisk = optarg; + break; } } command_line_len = 0; if (command_line) { - command_line_len = strlen(command_line) + 1; + command_line_len = strlen(command_line) + 16; } /* Parse the Elf file */ @@ -129,13 +140,45 @@ int elf_ia64_load(int argc, char **argv, /* Load the Elf data */ result = elf_exec_load(&ehdr, info); - free_elf_info(&ehdr); if (result < 0) { fprintf(stderr, "ELF load failed\n"); return result; } + + + /* Load the setup code */ + elf_rel_build_load(info, &info->rhdr, purgatory, purgatory_size, + 0x80000, ULONG_MAX, 1); + + if (command_line_len) { + char *cmdline = xmalloc(command_line_len); + strcpy(cmdline, command_line); + command_line_len = (command_line_len + 15)&(~15); + elf_rel_set_symbol(&info->rhdr, "__command_line_len", + &command_line_len, sizeof(long)); + command_line_base = add_buffer(info, cmdline, + command_line_len, command_line_len, + 16, 0, max_addr, 1); + elf_rel_set_symbol(&info->rhdr, "__command_line", + &command_line_base, sizeof(long)); + } - /* For now we don't have arguments to pass :( */ - info->entry = (void *)entry; + if (ramdisk) { + ramdisk_buf = slurp_file(ramdisk, &ramdisk_size); + ramdisk_base = add_buffer(info, ramdisk_buf, ramdisk_size, + ramdisk_size, + getpagesize(), 0, max_addr, 1); + elf_rel_set_symbol(&info->rhdr, "__ramdisk_base", + &ramdisk_base, sizeof(long)); + elf_rel_set_symbol(&info->rhdr, "__ramdisk_size", + &ramdisk_size, sizeof(long)); + } + + gp_value = info->rhdr.rel_addr + 0x200000; + elf_rel_set_symbol(&info->rhdr, "__gp_value", &gp_value, + sizeof(gp_value)); + + elf_rel_set_symbol(&info->rhdr, "__kernel_entry", &entry, sizeof(entry)); + free_elf_info(&ehdr); return 0; } diff -Nraup a/kexec/arch/ia64/kexec-elf-rel-ia64.c b/kexec/arch/ia64/kexec-elf-rel-ia64.c --- a/kexec/arch/ia64/kexec-elf-rel-ia64.c 2004-12-21 06:43:23.000000000 +0800 +++ b/kexec/arch/ia64/kexec-elf-rel-ia64.c 2005-11-09 03:39:16.000000000 +0800 @@ -1,8 +1,14 @@ +/* Most of the code in this file is + * based on arch/ia64/kernel/module.c in Linux kernel + */ + #include #include #include "../../kexec.h" #include "../../kexec-elf.h" +#define MAX_LTOFF ((uint64_t) (1 << 22)) + int machine_verify_elf_rel(struct mem_ehdr *ehdr) { if (ehdr->ei_data != ELFDATA2LSB) { @@ -17,12 +23,40 @@ int machine_verify_elf_rel(struct mem_eh return 1; } +static void +ia64_patch (uint64_t insn_addr, uint64_t mask, uint64_t val) +{ + uint64_t m0, m1, v0, v1, b0, b1, *b = (uint64_t *) (insn_addr & -16); +# define insn_mask ((1UL << 41) - 1) + unsigned long shift; + + b0 = b[0]; b1 = b[1]; + shift = 5 + 41 * (insn_addr % 16); /* 5 bits of template, then 3 x 41-bit instructions */ + if (shift >= 64) { + m1 = mask << (shift - 64); + v1 = val << (shift - 64); + } else { + m0 = mask << shift; m1 = mask >> (64 - shift); + v0 = val << shift; v1 = val >> (64 - shift); + b[0] = (b0 & ~m0) | (v0 & m0); + } + b[1] = (b1 & ~m1) | (v1 & m1); +} + +static inline uint64_t +bundle (const uint64_t insn) +{ + return insn & ~0xfUL; +} + void machine_apply_elf_rel(struct mem_ehdr *ehdr, unsigned long r_type, void *location, unsigned long address, unsigned long value) { + uint64_t gp_value = ehdr->rel_addr + 0x200000; switch(r_type) { case R_IA64_NONE: break; + case R_IA64_SEGREL64LSB: case R_IA64_DIR64LSB: *((uint64_t *)location) = value; break; @@ -31,15 +65,67 @@ void machine_apply_elf_rel(struct mem_eh if (value != *((uint32_t *)location)) goto overflow; break; - case R_IA64_PCREL21B: + case R_IA64_IMM64: + ia64_patch((uint64_t)location, 0x01fffefe000UL, + /* bit 63 -> 36 */ + (((value & 0x8000000000000000UL) >> 27) + /* bit 21 -> 21 */ + | ((value & 0x0000000000200000UL) << 0) + /* bit 16 -> 22 */ + | ((value & 0x00000000001f0000UL) << 6) + /* bit 7 -> 27 */ + | ((value & 0x000000000000ff80UL) << 20) + /* bit 0 -> 13 */ + | ((value & 0x000000000000007fUL) << 13))); + ia64_patch((uint64_t)location - 1, 0x1ffffffffffUL, value>>22); + break; + case R_IA64_IMM22: + if (value + (1 << 21) >= (1 << 22)) + die("value out of IMM22 range\n"); + ia64_patch((uint64_t)location, 0x01fffcfe000UL, + /* bit 21 -> 36 */ + (((value & 0x200000UL) << 15) + /* bit 16 -> 22 */ + | ((value & 0x1f0000UL) << 6) + /* bit 7 -> 27 */ + | ((value & 0x00ff80UL) << 20) + /* bit 0 -> 13 */ + | ((value & 0x00007fUL) << 13) )); + break; + case R_IA64_PCREL21B: { + uint64_t delta = ((int64_t)value - (int64_t)address)/16; + if (delta + (1 << 20) >= (1 << 21)) + die("value out of IMM21B range\n"); + value = ((int64_t)(value - bundle(address)))/16; + ia64_patch((uint64_t)location, 0x11ffffe000UL, + (((value & 0x100000UL) << 16) /* bit 20 -> 36 */ + | ((value & 0x0fffffUL) << 13) /* bit 0 -> 13 */)); + } + break; + case R_IA64_LTOFF22X: + if (value - gp_value + MAX_LTOFF/2 >= MAX_LTOFF) + die("value out of gp relative range"); + value -= gp_value; + ia64_patch((uint64_t)location, 0x01fffcfe000UL, + (((value & 0x200000UL) << 15) /* bit 21 -> 36 */ + |((value & 0x1f0000UL) << 6) /* bit 16 -> 22 */ + |((value & 0x00ff80UL) << 20) /* bit 7 -> 27 */ + |((value & 0x00007fUL) << 13) /* bit 0 -> 13 */)); + break; + case R_IA64_LDXMOV: + if (value - gp_value + MAX_LTOFF/2 >= MAX_LTOFF) + die("value out of gp relative range"); + ia64_patch((uint64_t)location, 0x1fff80fe000UL, 0x10000000000UL); + break; case R_IA64_LTOFF22: - case R_IA64_SEGREL64LSB: + default: - die("Unknown rela relocation: %lu\n", r_type); + die("Unknown rela relocation: 0x%lx 0x%lx\n", + r_type, address); break; } return; - overflow: +overflow: die("overflow in relocation type %lu val %Lx\n", - r_type, value); + r_type, value); } diff -Nraup a/kexec/arch/ia64/kexec-ia64.c b/kexec/arch/ia64/kexec-ia64.c --- a/kexec/arch/ia64/kexec-ia64.c 2005-01-11 14:28:36.000000000 +0800 +++ b/kexec/arch/ia64/kexec-ia64.c 2005-11-09 03:41:06.000000000 +0800 @@ -27,6 +27,7 @@ #include #include #include +#include #include #include "../../kexec.h" #include "../../kexec-syscall.h" @@ -56,7 +57,7 @@ int get_memory_ranges(struct memory_rang */ fprintf(stderr, "Warning assuming memory at 0-64MB is present\n"); memory_ranges = 0; - memory_range[memory_ranges].start = 0x00010000; + memory_range[memory_ranges].start = 0x00100000; memory_range[memory_ranges].end = 0x10000000; memory_range[memory_ranges].type = RANGE_RAM; memory_ranges++; @@ -76,9 +77,6 @@ void arch_usage(void) { } -static struct { -} arch_options = { -}; int arch_process_options(int argc, char **argv) { static const struct option options[] = { @@ -87,8 +85,11 @@ int arch_process_options(int argc, char }; static const char short_options[] = KEXEC_ARCH_OPT_STR; int opt; - unsigned long value; - char *end; + /* execute from BP */ + cpu_set_t affinity; + CPU_ZERO(&affinity); + CPU_SET(0, &affinity); + sched_setaffinity(0, sizeof(affinity), &affinity); opterr = 0; /* Don't complain about unrecognized options here */ while((opt = getopt_long(argc, argv, short_options, options, 0)) != -1) { @@ -115,32 +116,7 @@ int arch_compat_trampoline(struct kexec_ } if (strcmp(utsname.machine, "ia64") == 0) { - *flags |= KEXEC_ARCH_X86_64; - } - else { - fprintf(stderr, "Unsupported machine type: %s\n", - utsname.machine); - return -1; - } - return 0; -} - -int arch_compat_trampoline(struct kexec_info *info, unsigned long *flags) -{ - int result; - struct utsname utsname; - result = uname(&utsname); - if (result < 0) { - fprintf(stderr, "uname failed: %s\n", - strerror(errno)); - return -1; - } - if (strcmp(utsname.machine, "ia64") == 0) - { - /* For compatibility with older patches - * use KEXEC_ARCH_DEFAULT instead of KEXEC_ARCH_IA64 here. - */ - *flags |= KEXEC_ARCH_DEFAULT; + *flags |= KEXEC_ARCH_IA_64; } else { fprintf(stderr, "Unsupported machine type: %s\n", diff -Nraup a/kexec/kexec.c b/kexec/kexec.c --- a/kexec/kexec.c 2005-01-13 21:24:29.000000000 +0800 +++ b/kexec/kexec.c 2005-11-09 03:39:16.000000000 +0800 @@ -187,7 +187,7 @@ unsigned long locate_hole(struct kexec_i } /* Compute the free memory ranges */ - max_mem_ranges = memory_ranges + (info->nr_segments -1); + max_mem_ranges = memory_ranges + info->nr_segments; mem_range = malloc(max_mem_ranges *sizeof(struct memory_range)); mem_ranges = 0; diff -Nraup a/purgatory/arch/ia64/entry.S b/purgatory/arch/ia64/entry.S --- a/purgatory/arch/ia64/entry.S 1970-01-01 08:00:00.000000000 +0800 +++ b/purgatory/arch/ia64/entry.S 2005-11-09 03:39:16.000000000 +0800 @@ -0,0 +1,85 @@ +/* + * purgatory: setup code + * + * Copyright (C) 2005 Zou Nan hai (nanhai.zou@intel.com) + * + * 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 (version 2 of the License). + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +.global __dummy_efi_function +.align 32 +.proc __dummy_efi_function +__dummy_efi_function: + mov r8=r0;; + br.ret.sptk.many rp;; +.global __dummy_efi_function_end +__dummy_efi_function_end: +.endp __dummy_efi_function + +.global purgatory_start +.align 32 +.proc purgatory_start +purgatory_start: + movl r2=__gp_value;; + ld8 gp=[r2];; + br.call.sptk.many b0=purgatory + ;; + alloc r2 = ar.pfs, 0, 0, 5, 0 + ;; + mov out0=r28 + + movl r2=__command_line;; + ld8 out1=[r2];; + movl r2=__command_line_len;; + ld8 out2=[r2];; + movl r2=__ramdisk_base;; + ld8 out3=[r2];; + movl r2=__ramdisk_size;; + ld8 out4=[r2];; + br.call.sptk.many b0=ia64_env_setup + movl r10=__kernel_entry;; + ld8 r14=[r10];; + mov b6=r14;; + mov ar.lc=r0 + mov ar.ec=r0 + cover;; + invala;; + br.call.sptk.many b0=b6 +.endp purgatory_start + +.align 32 +.global __kernel_entry +.size __kernel_entry, 8 +__kernel_entry: + data8 0x0 +.global __command_line +.size __command_line, 8 +__command_line: + data8 0x0 +.global __command_line_len +.size __command_line_len, 8 +__command_line_len: + data8 0x0 +.global __ramdisk_base +.size __ramdisk_base, 8 +__ramdisk_base: + data8 0x0 +.global __ramdisk_size +.size __ramdisk_size, 8 +__ramdisk_size: + data8 0x0 +.global __gp_value +.size __gp_value, 8 +__gp_value: + data8 0x0 diff -Nraup a/purgatory/arch/ia64/Makefile b/purgatory/arch/ia64/Makefile --- a/purgatory/arch/ia64/Makefile 2004-12-21 06:44:22.000000000 +0800 +++ b/purgatory/arch/ia64/Makefile 2005-11-09 03:39:35.000000000 +0800 @@ -1,8 +1,8 @@ # # Purgatory ia64 # - -PURGATORY_S_SRCS+= +PCFLAGS += -ffixed-r28 +PURGATORY_S_SRCS+= purgatory/arch/ia64/entry.S PURGATORY_C_SRCS+= purgatory/arch/ia64/purgatory-ia64.c PURGATORY_C_SRCS+= purgatory/arch/ia64/console-ia64.c PURGATORY_C_SRCS+= diff -Nraup a/purgatory/arch/ia64/purgatory-ia64.c b/purgatory/arch/ia64/purgatory-ia64.c --- a/purgatory/arch/ia64/purgatory-ia64.c 2004-12-21 06:45:21.000000000 +0800 +++ b/purgatory/arch/ia64/purgatory-ia64.c 2005-11-09 03:39:16.000000000 +0800 @@ -1,7 +1,113 @@ #include +#include +#include #include "purgatory-ia64.h" +#define PAGE_OFFSET 0xe000000000000000 + +typedef struct { + uint64_t signature; + uint32_t revision; + uint32_t headersize; + uint32_t crc32; + uint32_t reserved; +} efi_table_hdr_t; + +typedef struct { + efi_table_hdr_t hdr; + unsigned long get_time; + unsigned long set_time; + unsigned long get_wakeup_time; + unsigned long set_wakeup_time; + unsigned long set_virtual_address_map; + unsigned long convert_pointer; + unsigned long get_variable; + unsigned long get_next_variable; + unsigned long set_variable; + unsigned long get_next_high_mono_count; + unsigned long reset_system; +} efi_runtime_services_t; + +typedef struct { + efi_table_hdr_t hdr; + unsigned long fw_vendor; /* physical addr of CHAR16 vendor string + */ + uint32_t fw_revision; + unsigned long con_in_handle; + unsigned long con_in; + unsigned long con_out_handle; + unsigned long con_out; + unsigned long stderr_handle; + unsigned long stderr; + unsigned long runtime; + unsigned long boottime; + unsigned long nr_tables; + unsigned long tables; +} efi_system_table_t; + +struct ia64_boot_param { + uint64_t command_line; /* physical address of command line arguments */ + uint64_t efi_systab; /* physical address of EFI system table */ + uint64_t efi_memmap; /* physical address of EFI memory map */ + uint64_t efi_memmap_size; /* size of EFI memory map */ + uint64_t efi_memdesc_size; /* size of an EFI memory map descriptor */ + uint32_t efi_memdesc_version; /* memory descriptor version */ + struct { + uint16_t num_cols; /* number of columns on console output device */ + uint16_t num_rows; /* number of rows on console output device */ + uint16_t orig_x; /* cursor's x position */ + uint16_t orig_y; /* cursor's y position */ + } console_info; + uint64_t fpswa; /* physical address of the fpswa interface */ + uint64_t initrd_start; + uint64_t initrd_size; +}; + void setup_arch(void) { /* Nothing for now */ } +inline unsigned long PA(unsigned long addr) +{ + return addr - PAGE_OFFSET; +} + +void flush_icache_range(char *start, unsigned long len) +{ + unsigned long i; + for (i = 0;i < len; i += 32) + asm volatile("fc.i %0"::"r"(start+i):"memory"); + asm volatile (";;sync.i;;":::"memory"); + asm volatile ("srlz.i":::"memory"); +} + +extern char __dummy_efi_function[], __dummy_efi_function_end[]; + +void ia64_env_setup(struct ia64_boot_param *boot_param, + uint64_t command_line, uint64_t command_line_len, + uint64_t ramdisk_base, uint64_t ramdisk_size) +{ + unsigned long len; + efi_system_table_t *systab; + efi_runtime_services_t *runtime; + unsigned long *set_virtual_address_map; + + // patch efi_runtime->set_virtual_address_map to a + // dummy function + len = __dummy_efi_function_end - __dummy_efi_function; + memcpy((char *)command_line + command_line_len, __dummy_efi_function, + len); + systab = (efi_system_table_t *)boot_param->efi_systab; + runtime = (efi_runtime_services_t *)PA(systab->runtime); + set_virtual_address_map = + (unsigned long *)PA(runtime->set_virtual_address_map); + *(set_virtual_address_map)= + (unsigned long)((char *)command_line + command_line_len); + flush_icache_range((char *)command_line+command_line_len, len); + + boot_param->command_line = command_line; + boot_param->console_info.orig_x = 0; + boot_param->console_info.orig_y = 0; + boot_param->initrd_start = ramdisk_base; + boot_param->initrd_size = ramdisk_size; +}