diff -Nraup a/kexec/arch/ia64/crashdump-ia64.c b/kexec/arch/ia64/crashdump-ia64.c --- a/kexec/arch/ia64/crashdump-ia64.c 1970-01-01 08:00:00.000000000 +0800 +++ b/kexec/arch/ia64/crashdump-ia64.c 2006-06-03 11:56:58.000000000 +0800 @@ -0,0 +1,351 @@ +/* + * kexec: crashdum support + * Copyright (C) 2005-2006 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 + * 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. + */ +#include +#include +#include +#include +#include +#include +#include "../../kexec.h" +#include "../../kexec-elf.h" +#include "../../kexec-syscall.h" +#include "kexec-ia64.h" +#include "crashdump-ia64.h" + +int memory_ranges = 0; +#define LOAD_OFFSET (0xa000000000000000UL + 0x100000000UL - (1UL<<26)) +#define MAX_LINE 160 +/* Stores a sorted list of RAM memory ranges for which to create elf headers. + * A separate program header is created for backup region */ +static struct memory_range crash_memory_range[CRASH_MAX_MEMORY_RANGES]; +/* Memory region reserved for storing panic kernel and other data. */ +static struct memory_range crash_reserved_mem; +unsigned long elfcorehdr; +static unsigned long kernel_code_start; +struct loaded_segment { + unsigned long start; + unsigned long end; + unsigned long reserved; +}; + +#define MAX_LOAD_SEGMENTS 128 +struct loaded_segment loaded_segments[MAX_LOAD_SEGMENTS]; + +unsigned long loaded_segments_num, loaded_segments_base; +static int seg_comp(const void *a, const void *b) +{ + const struct loaded_segment *x = a, *y = b; + /* avoid overflow */ + if (x->start > y->start) return 1; + if (x->start < y->start) return -1; + return 0; +} + +/* purgatory code need this info to patch the EFI memmap + */ +static void add_loaded_segments_info(struct kexec_info *info, + struct mem_ehdr *ehdr, unsigned long max_addr) +{ + int i; + for(i = 0; i < ehdr->e_phnum; i++) { + unsigned long start, end; + struct mem_phdr *phdr; + phdr = &ehdr->e_phdr[i]; + if (phdr->p_type != PT_LOAD) + continue; + start = phdr->p_paddr; + end = phdr->p_paddr + phdr->p_memsz; + + loaded_segments[loaded_segments_num].start = + start&~(ELF_PAGE_SIZE-1); + loaded_segments[loaded_segments_num].end = + (end + ELF_PAGE_SIZE - 1)&~(ELF_PAGE_SIZE - 1); + loaded_segments[loaded_segments_num].reserved = 0; + loaded_segments_num++; + } +} + +static int get_crash_notes_section_addr(unsigned long *addr, int cpu) +{ + char crash_notes[128]; + char line[MAX_LINE]; + FILE *fp; + sprintf(crash_notes, "/sys/devices/system/cpu/cpu%d/crash_notes", cpu); + fp = fopen(crash_notes, "r"); + if (!fp) { + fprintf(stderr, "Cannot open %s: %s\n", + crash_notes, strerror(errno)); + fprintf(stderr, "Try mounting sysfs\n"); + return -1; + } + if (fscanf(fp, "%lx", addr) != 1) { + *addr = 0; + return -1; + } + return 0; +} + +/* Removes crash reserve region from list of memory chunks for whom elf program + * headers have to be created. Assuming crash reserve region to be a single + * continuous area fully contained inside one of the memory chunks */ +static int exclude_crash_reserve_region(int *nr_ranges) +{ + int i, j, tidx = -1; + unsigned long cstart, cend; + struct memory_range temp_region; + + /* Crash reserved region. */ + cstart = crash_reserved_mem.start; + cend = crash_reserved_mem.end; + + for (i = 0; i < (*nr_ranges); i++) { + unsigned long mstart, mend; + mstart = crash_memory_range[i].start; + mend = crash_memory_range[i].end; + if (cstart < mend && cend > mstart) { + if (cstart != mstart && cend != mend) { + /* Split memory region */ + crash_memory_range[i].end = cstart - 1; + temp_region.start = cend + 1; + temp_region.end = mend; + temp_region.type = RANGE_RAM; + tidx = i+1; + } else if (cstart != mstart) + crash_memory_range[i].end = cstart - 1; + else + crash_memory_range[i].start = cend + 1; + } + } + /* Insert split memory region, if any. */ + if (tidx >= 0) { + if (*nr_ranges == CRASH_MAX_MEMORY_RANGES) { + /* No space to insert another element. */ + fprintf(stderr, "Error: Number of crash memory ranges" + " excedeed the max limit\n"); + return -1; + } + for (j = (*nr_ranges - 1); j >= tidx; j--) + crash_memory_range[j+1] = crash_memory_range[j]; + crash_memory_range[tidx].start = temp_region.start; + crash_memory_range[tidx].end = temp_region.end; + crash_memory_range[tidx].type = temp_region.type; + (*nr_ranges)++; + } + return 0; +} + +static int prepare_crash_memory_elf64_headers(struct kexec_info *info, + void *buf, unsigned long size) +{ + Elf64_Ehdr *elf; + Elf64_Phdr *phdr; + int i; + long int nr_cpus = 0; + char *bufp = buf; + unsigned long notes_addr, notes_offset; + + /* Setup ELF Header*/ + elf = (Elf64_Ehdr *) bufp; + bufp += sizeof(Elf64_Ehdr); + memcpy(elf->e_ident, ELFMAG, SELFMAG); + elf->e_ident[EI_CLASS] = ELFCLASS64; + elf->e_ident[EI_DATA] = ELFDATA2LSB; + elf->e_ident[EI_VERSION]= EV_CURRENT; + elf->e_ident[EI_OSABI] = ELFOSABI_NONE; + memset(elf->e_ident+EI_PAD, 0, EI_NIDENT-EI_PAD); + elf->e_type = ET_CORE; + elf->e_machine = EM_IA_64; + elf->e_version = EV_CURRENT; + elf->e_entry = 0; + elf->e_phoff = sizeof(Elf64_Ehdr); + elf->e_shoff = 0; + elf->e_flags = 0; + elf->e_ehsize = sizeof(Elf64_Ehdr); + elf->e_phentsize= sizeof(Elf64_Phdr); + elf->e_phnum = 0; + elf->e_shentsize= 0; + elf->e_shnum = 0; + elf->e_shstrndx = 0; + + /* PT_NOTE program headers. One per cpu*/ + nr_cpus = sysconf(_SC_NPROCESSORS_CONF); + if (nr_cpus < 0) { + return -1; + } + + /* Need to find a better way to determine per cpu notes section size. */ +#define MAX_NOTE_BYTES 1024 + + for (i = 0; i < nr_cpus; i++) { + if (get_crash_notes_section_addr (¬es_addr, i) < 0) + break; + notes_offset = notes_addr; + phdr = (Elf64_Phdr *) bufp; + bufp += sizeof(Elf64_Phdr); + phdr->p_type = PT_NOTE; + phdr->p_flags = 0; + phdr->p_offset = notes_offset; + phdr->p_vaddr = phdr->p_paddr = notes_offset; + phdr->p_filesz = phdr->p_memsz = MAX_NOTE_BYTES; + /* Do we need any alignment of segments? */ + phdr->p_align = 0; + + /* Increment number of program headers. */ + (elf->e_phnum)++; + } + + for (i = 0; i < memory_ranges; i++) { + unsigned long mstart, mend; + mstart = crash_memory_range[i].start; + mend = crash_memory_range[i].end; + if (!mstart && !mend) + break; + phdr = (Elf64_Phdr *) bufp; + bufp += sizeof(Elf64_Phdr); + phdr->p_type = PT_LOAD; + phdr->p_flags = PF_R|PF_W|PF_X; + phdr->p_offset = mstart; + /*add region 5 mapping for kernel*/ + if (kernel_code_start >= mstart && kernel_code_start < mend) { + phdr->p_vaddr = mstart + LOAD_OFFSET; + phdr->p_paddr = mstart; + phdr->p_filesz = phdr->p_memsz = mend - mstart + 1; + phdr->p_align = 0; + (elf->e_phnum)++; + + phdr = (Elf64_Phdr *) bufp; + bufp += sizeof(Elf64_Phdr); + phdr->p_type = PT_LOAD; + phdr->p_flags = PF_R|PF_W|PF_X; + phdr->p_offset = mstart; + } + phdr->p_vaddr = mstart + PAGE_OFFSET; + phdr->p_paddr = mstart; + phdr->p_filesz = phdr->p_memsz = mend - mstart + 1; + phdr->p_align = 0; + (elf->e_phnum)++; + } + return 0; +} + +static int get_crash_memory_ranges(struct memory_range **range, int *ranges) +{ + const char iomem[]= "/proc/iomem"; + char line[MAX_LINE]; + FILE *fp; + unsigned long start, end; + + fp = fopen(iomem, "r"); + if (!fp) { + fprintf(stderr, "Cannot open %s: %s\n", + iomem, strerror(errno)); + return -1; + } + while(fgets(line, sizeof(line), fp) != 0) { + char *str; + int type, consumed, count; + if (memory_ranges >= CRASH_MAX_MEMORY_RANGES) + break; + count = sscanf(line, "%lx-%lx : %n", + &start, &end, &consumed); + str = line + consumed; + if (count != 2) + continue; + + if (memcmp(str, "System RAM\n", 11) == 0) { + type = RANGE_RAM; + } else if (memcmp(str, "Crash kernel\n", 13) == 0) { + /* Reserved memory region. New kernel can + * use this region to boot into. */ + crash_reserved_mem.start = start; + crash_reserved_mem.end = end; + crash_reserved_mem.type = RANGE_RAM; + continue; + } + else if (memcmp(str, "Kernel code\n", 12) == 0) { + kernel_code_start = start; + continue; + }else + continue; + crash_memory_range[memory_ranges].start = start; + crash_memory_range[memory_ranges].end = end; + crash_memory_range[memory_ranges].type = type; + memory_ranges++; + } + fclose(fp); + if (exclude_crash_reserve_region(&memory_ranges) < 0) + return -1; + *ranges = memory_ranges; + return 0; +} + +static void +cmdline_add_elfcorehdr(char **cmdline, unsigned long addr) +{ + char *str = *cmdline; + char buf[64]; + size_t len; + sprintf(buf, " elfcorehdr=%ldK", addr/1024); + len = strlen(str) + strlen(buf) + 1; + str = xmalloc(len); + sprintf(str, "%s%s", *cmdline, buf); + *cmdline = str; +} + +int load_crashdump_segments(struct kexec_info *info, struct mem_ehdr *ehdr, + unsigned long max_addr, unsigned long min_base, + char **cmdline) +{ + //struct memory_range *mem_range, *memmap_p; + struct memory_range *mem_range; + int nr_ranges; + size_t size; + void *tmp; + if (info->kexec_flags & KEXEC_ON_CRASH ) { + if (get_crash_memory_ranges(&mem_range, &nr_ranges) == 0) { + size = sizeof(Elf64_Ehdr) + + (nr_ranges + 1) * sizeof(Elf64_Phdr); + size = (size + EFI_PAGE_SIZE - 1) & ~(EFI_PAGE_SIZE - 1); + tmp = xmalloc(size); + memset(tmp, 0, size); + if (prepare_crash_memory_elf64_headers(info, tmp, size) < 0) + return -1; + elfcorehdr = add_buffer(info, tmp, size, size, EFI_PAGE_SIZE, min_base, + max_addr, -1); + loaded_segments[loaded_segments_num].start = elfcorehdr; + loaded_segments[loaded_segments_num].end = elfcorehdr + size; + loaded_segments[loaded_segments_num].reserved = 1; + loaded_segments_num++; + cmdline_add_elfcorehdr(cmdline, elfcorehdr); + } + } + add_loaded_segments_info(info, ehdr, max_addr); + size = sizeof(struct loaded_segment) * loaded_segments_num; + qsort(loaded_segments, loaded_segments_num, + sizeof(struct loaded_segment), seg_comp); + loaded_segments_base = add_buffer(info, loaded_segments, + size, size, 16, 0, max_addr, -1); + + elf_rel_set_symbol(&info->rhdr, "__loaded_segments", + &loaded_segments_base, sizeof(long)); + elf_rel_set_symbol(&info->rhdr, "__loaded_segments_num", + &loaded_segments_num, sizeof(long)); + return 0; +} + + diff -Nraup a/kexec/arch/ia64/crashdump-ia64.h b/kexec/arch/ia64/crashdump-ia64.h --- a/kexec/arch/ia64/crashdump-ia64.h 1970-01-01 08:00:00.000000000 +0800 +++ b/kexec/arch/ia64/crashdump-ia64.h 2006-06-03 11:56:58.000000000 +0800 @@ -0,0 +1,13 @@ +#ifndef CRASHDUMP_IA64_H +#define CRASHDUMP_IA64_H + +#define PAGE_OFFSET 0xe000000000000000UL +#define __pa(x) ((unsigned long)(x)-PAGE_OFFSET) +extern int load_crashdump_segments(struct kexec_info *info, + struct mem_ehdr *ehdr, unsigned long max_addr, + unsigned long min_base, char **cmdline); + +#define CRASH_MAX_MEMMAP_NR (KEXEC_MAX_SEGMENTS + 1) +#define CRASH_MAX_MEMORY_RANGES (MAX_MEMORY_RANGES + 2) + +#endif 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 2006-06-03 11:58:45.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,11 +35,14 @@ #include #include #include +#include #include #include #include #include "../../kexec.h" +#include "../../kexec-syscall.h" #include "../../kexec-elf.h" +#include "crashdump-ia64.h" #include static const int probe_debug = 0; @@ -74,48 +78,79 @@ 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"); +} + +/* Move the crash kerenl physical offset to reserved region + */ +static void move_loaded_segments(struct kexec_info *info, struct mem_ehdr *ehdr) +{ + int i; + long offset; + struct mem_phdr *phdr; + for(i = 0; i < ehdr->e_phnum; i++) { + phdr = &ehdr->e_phdr[i]; + if (phdr->p_type == PT_LOAD) { + offset = mem_min - phdr->p_paddr; + break; + } + } + ehdr->e_entry += offset; + for(i = 0; i < ehdr->e_phnum; i++) { + phdr = &ehdr->e_phdr[i]; + if (phdr->p_type == PT_LOAD) + phdr->p_paddr += offset; + } } 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; + char *command_line = 0, *ramdisk=0; + char *ramdisk_buf; + off_t ramdisk_size = 0; + unsigned long command_line_len; + unsigned long entry, max_addr, gp_value; + unsigned long command_line_base, ramdisk_base; + unsigned long efi_memmap_base, efi_memmap_size; int result; int opt; + char *efi_memmap_buf; #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}, }; static const char short_options[] = KEXEC_ARCH_OPT_STR ""; command_line = 0; - while ((opt = getopt_long(argc, argv, short_options, options, 0)) != -1) { + while ((opt = getopt_long(argc, argv, short_options, options, 0)) + != -1) { switch (opt) { - default: - /* Ignore core options */ - if (opt < OPT_ARCH_MAX) { + default: + /* Ignore core options */ + if (opt < OPT_ARCH_MAX) { + break; + } + case '?': + usage(); + return -1; + case OPT_APPEND: + command_line = optarg; + break; + case OPT_RAMDISK: + ramdisk = optarg; break; - } - case '?': - usage(); - return -1; - case OPT_APPEND: - command_line = optarg; - break; } } command_line_len = 0; - if (command_line) { - command_line_len = strlen(command_line) + 1; - } /* Parse the Elf file */ result = build_elf_exec_info(buf, len, &ehdr); @@ -124,18 +159,85 @@ int elf_ia64_load(int argc, char **argv, free_elf_info(&ehdr); return result; } + if (info->kexec_flags & KEXEC_ON_CRASH ) { + if ((mem_min == 0x00) && (mem_max = ULONG_MAX)) { + fprintf(stderr, "Failed to find crash kernel region in /proc/iomem\n"); + return -1; + } + move_loaded_segments(info, &ehdr); + } + entry = ehdr.e_entry; max_addr = elf_max_addr(&ehdr); - /* 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; } - - /* For now we don't have arguments to pass :( */ - info->entry = (void *)entry; + /* Load the setup code */ + elf_rel_build_load(info, &info->rhdr, purgatory, purgatory_size, + 0, ULONG_MAX, -1); + + if (load_crashdump_segments(info, &ehdr, max_addr, 0, + &command_line) < 0) + return -1; + + // reserve 8k for efi_memmap + efi_memmap_size = 1UL<<14; + efi_memmap_buf = xmalloc(efi_memmap_size); + efi_memmap_base = add_buffer(info, efi_memmap_buf, + efi_memmap_size, efi_memmap_size, 4096, 0, + max_addr, -1); + + elf_rel_set_symbol(&info->rhdr, "__efi_memmap_base", + &efi_memmap_base, sizeof(long)); + + elf_rel_set_symbol(&info->rhdr, "__efi_memmap_size", + &efi_memmap_size, sizeof(long)); + if (command_line) { + command_line_len = strlen(command_line) + 1; + } + if (command_line_len || (info->kexec_flags & KEXEC_ON_CRASH )) { + char *cmdline = xmalloc(command_line_len); + strcpy(cmdline, command_line); + + if (info->kexec_flags & KEXEC_ON_CRASH) { + char buf[128]; + sprintf(buf," max_addr=%lluM min_addr=%lluM", + mem_max>>20, mem_min>>20); + command_line_len = strlen(cmdline) + strlen(buf) + 1; + cmdline = xrealloc(cmdline, command_line_len); + strcat(cmdline, buf); + } + + command_line_len = (command_line_len + 15)&(~15); + command_line_base = add_buffer(info, cmdline, + command_line_len, command_line_len, + getpagesize(), 0UL, + max_addr, -1); + elf_rel_set_symbol(&info->rhdr, "__command_line_len", + &command_line_len, sizeof(long)); + elf_rel_set_symbol(&info->rhdr, "__command_line", + &command_line_base, sizeof(long)); + } + 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 2006-06-03 11:56:58.000000000 +0800 @@ -1,8 +1,32 @@ +/* + * Copyright (C) 2005-2006 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. + */ + +/* pugatory relocation code + * 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 +41,49 @@ 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 void +put_unaligned64(unsigned long val, unsigned char *location) +{ + unsigned char *src = (unsigned char *)&val; + int i; + for (i = 0; i < sizeof(long); i++) + *location++ = *src++; +} + +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 +92,72 @@ 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_PCREL64LSB: { + value = value - address; + put_unaligned64(value, location); + } break; + case R_IA64_GPREL22: + 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 2006-06-03 11:56:06.000000000 +0800 +++ b/kexec/arch/ia64/kexec-ia64.c 2006-06-03 12:00:38.000000000 +0800 @@ -6,6 +6,8 @@ * Copyright (C) 2004 Silicon Graphics, Inc. * Jesse Barnes * + * Copyright (C) 2005-2006 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 * the Free Software Foundation (version 2 of the License). @@ -27,40 +29,84 @@ #include #include #include +#include #include #include "../../kexec.h" #include "../../kexec-syscall.h" #include "kexec-ia64.h" #include -#define MAX_MEMORY_RANGES 64 #define MAX_LINE 160 static struct memory_range memory_range[MAX_MEMORY_RANGES]; /* Return a sorted list of available memory ranges. */ int get_memory_ranges(struct memory_range **range, int *ranges, - unsigned long kexec_flags) + unsigned long kexec_flags) { - int memory_ranges; - /* - * /proc/iomem on ia64 does not show where all memory is. If - * that is fixed up, we can make use of that to validate - * the memory range kernel will be loade din. Until then..... - * -- Khalid Aziz - */ - - /* Note that the ia64 architecture mandates all systems will - * have at least 64MB at 0-64M. The SGI altix does not follow - * that restriction, but a reasonable guess is better than nothing - * at all. - * -- Eric Biederman - */ - fprintf(stderr, "Warning assuming memory at 0-64MB is present\n"); - memory_ranges = 0; - memory_range[memory_ranges].start = 0x00010000; - memory_range[memory_ranges].end = 0x10000000; - memory_range[memory_ranges].type = RANGE_RAM; - memory_ranges++; + const char iomem[]= "/proc/iomem"; + int memory_ranges = 0; + char line[MAX_LINE]; + FILE *fp; + fp = fopen(iomem, "r"); + if (!fp) { + fprintf(stderr, "Cannot open %s: %s\n", + iomem, strerror(errno)); + return -1; + } + + while(fgets(line, sizeof(line), fp) != 0) { + unsigned long start, end; + char *str; + int type; + int consumed; + int count; + if (memory_ranges >= MAX_MEMORY_RANGES) + break; + count = sscanf(line, "%lx-%lx : %n", + &start, &end, &consumed); + if (count != 2) + continue; + str = line + consumed; + end = end + 1; + if (memcmp(str, "System RAM\n", 11) == 0) { + type = RANGE_RAM; + } + else if (memcmp(str, "reserved\n", 9) == 0) { + type = RANGE_RESERVED; + } + else if (memcmp(str, "Crash kernel\n", 13) == 0) { + /* Redefine the memory region boundaries if kernel + * exports the limits and if it is panic kernel. + * Override user values only if kernel exported values are + * subset of user defined values. + */ + + if (kexec_flags & KEXEC_ON_CRASH) { + if (start > mem_min) + mem_min = start; + if (end < mem_max) + mem_max = end; + } + continue; + } else + continue; + /* + * Check if this memory range can be coalesced with + * the previous range + */ + if ((memory_ranges > 0) && + (start == memory_range[memory_ranges-1].end) && + (type == memory_range[memory_ranges-1].type)) { + memory_range[memory_ranges-1].end = end; + } + else { + memory_range[memory_ranges].start = start; + memory_range[memory_ranges].end = end; + memory_range[memory_ranges].type = type; + memory_ranges++; + } + } + fclose(fp); *range = memory_range; *ranges = memory_ranges; return 0; @@ -77,9 +123,6 @@ void arch_usage(void) { } -static struct { -} arch_options = { -}; int arch_process_options(int argc, char **argv) { static const struct option options[] = { @@ -88,8 +131,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) { @@ -116,32 +162,7 @@ int arch_compat_trampoline(struct kexec_ } if (strcmp(utsname.machine, "ia64") == 0) { - info->kexec_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) -{ - 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. - */ - info->kexec_flags |= KEXEC_ARCH_DEFAULT; + info->kexec_flags |= KEXEC_ARCH_IA_64; } else { fprintf(stderr, "Unsupported machine type: %s\n", diff -Nraup a/kexec/arch/ia64/kexec-ia64.h b/kexec/arch/ia64/kexec-ia64.h --- a/kexec/arch/ia64/kexec-ia64.h 2004-12-20 07:52:38.000000000 +0800 +++ b/kexec/arch/ia64/kexec-ia64.h 2006-06-03 11:56:58.000000000 +0800 @@ -5,5 +5,7 @@ int elf_ia64_probe(const char *buf, off_ int elf_ia64_load(int argc, char **argv, const char *buf, off_t len, struct kexec_info *info); void elf_ia64_usage(void); - +#define MAX_MEMORY_RANGES 1024 +#define EFI_PAGE_SIZE (1UL<<12) +#define ELF_PAGE_SIZE (1UL<<16) #endif /* KEXEC_IA64_H */ diff -Nraup a/kexec/arch/ia64/Makefile b/kexec/arch/ia64/Makefile --- a/kexec/arch/ia64/Makefile 2004-12-20 08:11:13.000000000 +0800 +++ b/kexec/arch/ia64/Makefile 2006-06-03 11:56:58.000000000 +0800 @@ -4,3 +4,5 @@ KEXEC_C_SRCS+= kexec/arch/ia64/kexec-ia64.c KEXEC_C_SRCS+= kexec/arch/ia64/kexec-elf-ia64.c KEXEC_C_SRCS+= kexec/arch/ia64/kexec-elf-rel-ia64.c +KEXEC_C_SRCS+= kexec/arch/ia64/crashdump-ia64.c + diff -Nraup a/kexec/kexec.c b/kexec/kexec.c --- a/kexec/kexec.c 2006-06-03 11:56:06.000000000 +0800 +++ b/kexec/kexec.c 2006-06-03 11:56:58.000000000 +0800 @@ -96,6 +96,9 @@ int valid_memory_range(unsigned long sst continue; mstart = memory_range[i].start; mend = memory_range[i].end; + if (i < memory_ranges - 1 && mend == memory_range[i+1].start) + mend = memory_range[i+1].end; + /* Check to see if we are fully contained */ if ((mstart <= sstart) && (mend >= send)) { return 1; @@ -187,7 +190,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/kexec/kexec-syscall.h b/kexec/kexec-syscall.h --- a/kexec/kexec-syscall.h 2005-01-06 14:59:50.000000000 +0800 +++ b/kexec/kexec-syscall.h 2006-06-03 11:56:58.000000000 +0800 @@ -68,6 +68,6 @@ static inline long kexec_reboot(void) #define KEXEC_ARCH_PPC64 (21 << 16) #define KEXEC_ARCH_IA_64 (50 << 16) -#define KEXEC_MAX_SEGMENTS 8 +#define KEXEC_MAX_SEGMENTS 16 #endif /* KEXEC_SYSCALL_H */ diff -Nraup a/purgatory/arch/ia64/console-ia64.c b/purgatory/arch/ia64/console-ia64.c --- a/purgatory/arch/ia64/console-ia64.c 2004-12-21 06:41:26.000000000 +0800 +++ b/purgatory/arch/ia64/console-ia64.c 2006-06-03 11:56:58.000000000 +0800 @@ -1,5 +1,47 @@ #include +#include "io.h" + +#define VGABASE UNCACHED(0xb8000) + +/* code based on i386 console code + * TODO add serial support + */ +#define MAX_YPOS 25 +#define MAX_XPOS 80 + +unsigned long current_ypos = 1, current_xpos = 0; + +static void putchar_vga(int ch) +{ + int i, k, j; + + if (current_ypos >= MAX_YPOS) { + /* scroll 1 line up */ + for (k = 1, j = 0; k < MAX_YPOS; k++, j++) { + for (i = 0; i < MAX_XPOS; i++) { + writew(readw(VGABASE + 2*(MAX_XPOS*k + i)), + VGABASE + 2*(MAX_XPOS*j + i)); + } + } + for (i = 0; i < MAX_XPOS; i++) + writew(0x720, VGABASE + 2*(MAX_XPOS*j + i)); + current_ypos = MAX_YPOS-1; + } + if (ch == '\n') { + current_xpos = 0; + current_ypos++; + } else if (ch != '\r') { + writew(((0x7 << 8) | (unsigned short) ch), + VGABASE + 2*(MAX_XPOS*current_ypos + + current_xpos++)); + if (current_xpos >= MAX_XPOS) { + current_xpos = 0; + current_ypos++; + } + } +} + void putchar(int ch) { - /* Nothing for now */ + putchar_vga(ch); } 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 2006-06-03 11:56:58.000000000 +0800 @@ -0,0 +1,67 @@ +/* + * purgatory: setup code + * + * Copyright (C) 2005-2006 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. + */ +#define DECLARE_DATA8(name) \ +.global name; \ +.size name, 8; \ +name: data8 0x0 + +.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, 2, 0 + ;; + mov out0=r28 + movl out1=__ramdisk_base;; + 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 + +DECLARE_DATA8(__kernel_entry) +DECLARE_DATA8(__ramdisk_base) +DECLARE_DATA8(__ramdisk_size) +DECLARE_DATA8(__command_line) +DECLARE_DATA8(__command_line_len) +DECLARE_DATA8(__efi_memmap_base) +DECLARE_DATA8(__efi_memmap_size) +DECLARE_DATA8(__loaded_segments) +DECLARE_DATA8(__loaded_segments_num) + +DECLARE_DATA8(__gp_value) diff -Nraup a/purgatory/arch/ia64/io.h b/purgatory/arch/ia64/io.h --- a/purgatory/arch/ia64/io.h 1970-01-01 08:00:00.000000000 +0800 +++ b/purgatory/arch/ia64/io.h 2006-06-03 11:56:58.000000000 +0800 @@ -0,0 +1,94 @@ +#ifndef IO_H +#define IO_H +#define UNCACHED(x) (void *)((x)|(1UL<<63)) +#define MF() asm volatile ("mf.a" ::: "memory") +#define IO_SPACE_ENCODING(p) ((((p) >> 2) << 12) | (p & 0xfff)) + +static inline void *io_addr (unsigned long port) +{ + unsigned long offset; + unsigned long io_base; + asm volatile ("mov %0=ar.k0":"=r"(io_base)); + offset = IO_SPACE_ENCODING(port); + return UNCACHED(io_base | offset); +} + +static inline unsigned int inb (unsigned long port) +{ + volatile unsigned char *addr = io_addr(port); + unsigned char ret; + ret = *addr; + MF(); + return ret; +} + +static inline unsigned int inw (unsigned long port) +{ + volatile unsigned short *addr = io_addr(port); + unsigned short ret; + + ret = *addr; + MF(); + return ret; +} + +static inline unsigned int ia64_inl (unsigned long port) +{ + volatile unsigned int *addr = __ia64_mk_io_addr(port); + unsigned int ret; + ret = *addr; + MF(); + return ret; +} + +static inline void outb (unsigned char val, unsigned long port) +{ + volatile unsigned char *addr = io_addr(port); + + *addr = val; + MF(); +} + +static inline void outw (unsigned short val, unsigned long port) +{ + volatile unsigned short *addr = io_addr(port); + + *addr = val; + MF(); +} + +static inline void outl (unsigned int val, unsigned long port) +{ + volatile unsigned int *addr = io_addr(port); + + *addr = val; + MF(); +} + + +static inline unsigned char readb(const volatile void *addr) +{ + return *(volatile unsigned char *) addr; +} +static inline unsigned short readw(const volatile void *addr) +{ + return *(volatile unsigned short *) addr; +} +static inline unsigned int readl(const volatile void *addr) +{ + return *(volatile unsigned int *) addr; +} + +static inline void writeb(unsigned char b, volatile void *addr) +{ + *(volatile unsigned char *) addr = b; +} +static inline void writew(unsigned short b, volatile void *addr) +{ + *(volatile unsigned short *) addr = b; +} +static inline void writel(unsigned int b, volatile void *addr) +{ + *(volatile unsigned int *) addr = b; +} +#endif 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 2006-06-03 11:56:58.000000000 +0800 @@ -1,9 +1,9 @@ # # 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+= +PURGATORY_C_SRCS+= purgatory/arch/ia64/vga.c diff -Nraup a/purgatory/arch/ia64/purgatory-ia64.c b/purgatory/arch/ia64/purgatory-ia64.c --- a/purgatory/arch/ia64/purgatory-ia64.c 2006-06-03 11:56:06.000000000 +0800 +++ b/purgatory/arch/ia64/purgatory-ia64.c 2006-06-03 11:56:58.000000000 +0800 @@ -1,9 +1,271 @@ +/* + * purgatory: setup code + * + * Copyright (C) 2005-2006 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. + */ #include +#include +#include #include "purgatory-ia64.h" -void setup_arch(void) +#define PAGE_OFFSET 0xe000000000000000UL + +#define EFI_PAGE_SHIFT 12 +#define EFI_PAGE_SIZE (1UL<efi_memmap_base; + void *src = (void *)boot_param->efi_memmap; + unsigned long len = boot_param->efi_memmap_size; + unsigned long memdesc_size = boot_param->efi_memdesc_size; + uint64_t orig_type; + efi_memory_desc_t *md1, *md2; + void *p1, *p2, *src_end = src + len; + int i; + for (p1 = src, p2 = dest; p1 < src_end; + p1 += memdesc_size, p2 += memdesc_size) { + unsigned long mstart, mend; + md1 = p1; + md2 = p2; + if (md1->num_pages == 0) + continue; + mstart = md1->phys_addr; + mend = md1->phys_addr + (md1->num_pages + << EFI_PAGE_SHIFT); + switch (md1->type) { + case EFI_LOADER_DATA: + *md2 = *md1; + md2->type = EFI_CONVENTIONAL_MEMORY; + break; + default: + *md2 = *md1; + } + // segments are already sorted and aligned to 4K + orig_type = md2->type; + for (i = 0; i < params->loaded_segments_num; i++) { + struct loaded_segment *seg; + seg = ¶ms->loaded_segments[i]; + if (seg->start >= mstart && seg->start < mend) { + unsigned long start_pages, mid_pages, end_pages; + if (seg->end > mend) { + p1 += memdesc_size; + for(; p1 < src_end; + p1 += memdesc_size) { + md1 = p1; + /* TODO check contig and attribute here */ + mend = md1->phys_addr + + (md1->num_pages << EFI_PAGE_SHIFT); + if (seg->end < mend) + break; + } + } + start_pages = (seg->start - mstart) + >> EFI_PAGE_SHIFT; + mid_pages = (seg->end - seg->start) + >> EFI_PAGE_SHIFT; + end_pages = (mend - seg->end) + >> EFI_PAGE_SHIFT; + if (start_pages) { + md2->num_pages = start_pages; + p2 += memdesc_size; + md2 = p2; + *md2 = *md1; + } + md2->phys_addr = seg->start; + md2->num_pages = mid_pages; + md2->type = seg->reserved ? + EFI_UNUSABLE_MEMORY:EFI_LOADER_DATA; + if (end_pages) { + p2 += memdesc_size; + md2 = p2; + *md2 = *md1; + md2->phys_addr = seg->end; + md2->num_pages = end_pages; + md2->type = orig_type; + mstart = seg->end; + } else + break; + } + } + } + + boot_param->efi_memmap_size = p2 - dest; +} + +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, + struct kexec_boot_params *params) +{ + unsigned long len; + efi_system_table_t *systab; + efi_runtime_services_t *runtime; + unsigned long *set_virtual_address_map; + char *command_line = (char *)params->command_line; + uint64_t command_line_len = params->command_line_len; + + // patch efi_runtime->set_virtual_address_map to a + // dummy function + len = __dummy_efi_function_end - __dummy_efi_function; + memcpy(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)(command_line + command_line_len); + flush_icache_range(command_line + command_line_len, len); + + patch_efi_memmap(params, boot_param); + + boot_param->efi_memmap = params->efi_memmap_base; + + boot_param->command_line = params->command_line; + boot_param->console_info.orig_x = 0; + boot_param->console_info.orig_y = 0; + boot_param->initrd_start = params->ramdisk_base; + boot_param->initrd_size = params->ramdisk_size; } /* This function can be used to execute after the SHA256 verification. */ diff -Nraup a/purgatory/arch/ia64/purgatory-ia64.h b/purgatory/arch/ia64/purgatory-ia64.h --- a/purgatory/arch/ia64/purgatory-ia64.h 2004-12-21 06:44:55.000000000 +0800 +++ b/purgatory/arch/ia64/purgatory-ia64.h 2006-06-03 11:56:58.000000000 +0800 @@ -1,6 +1,5 @@ #ifndef PURGATORY_IA64_H #define PURGATORY_IA64_H -/* nothing yet */ - +void reset_vga(void); #endif /* PURGATORY_IA64_H */ diff -Nraup a/purgatory/arch/ia64/vga.c b/purgatory/arch/ia64/vga.c --- a/purgatory/arch/ia64/vga.c 1970-01-01 08:00:00.000000000 +0800 +++ b/purgatory/arch/ia64/vga.c 2006-06-03 11:56:58.000000000 +0800 @@ -0,0 +1,143 @@ +#include "io.h" +void reset_vga(void) +{ + /* Hello */ + inb(0x3da); + outb(0, 0x3c0); + + /* Sequencer registers */ + outw(0x0300, 0x3c4); + outw(0x0001, 0x3c4); + outw(0x0302, 0x3c4); + outw(0x0003, 0x3c4); + outw(0x0204, 0x3c4); + + /* Ensure CRTC regs 0-7 are unlocked by clearing bit 7 of CRTC[17] */ + outw(0x0e11, 0x3d4); + /* CRTC registers */ + outw(0x5f00, 0x3d4); + outw(0x4f01, 0x3d4); + outw(0x5002, 0x3d4); + outw(0x8203, 0x3d4); + outw(0x5504, 0x3d4); + outw(0x8105, 0x3d4); + outw(0xbf06, 0x3d4); + outw(0x1f07, 0x3d4); + outw(0x0008, 0x3d4); + outw(0x4f09, 0x3d4); + outw(0x200a, 0x3d4); + outw(0x0e0b, 0x3d4); + outw(0x000c, 0x3d4); + outw(0x000d, 0x3d4); + outw(0x010e, 0x3d4); + outw(0xe00f, 0x3d4); + outw(0x9c10, 0x3d4); + outw(0x8e11, 0x3d4); + outw(0x8f12, 0x3d4); + outw(0x2813, 0x3d4); + outw(0x1f14, 0x3d4); + outw(0x9615, 0x3d4); + outw(0xb916, 0x3d4); + outw(0xa317, 0x3d4); + outw(0xff18, 0x3d4); + + /* Graphic registers */ + outw(0x0000, 0x3ce); + outw(0x0001, 0x3ce); + outw(0x0002, 0x3ce); + outw(0x0003, 0x3ce); + outw(0x0004, 0x3ce); + outw(0x1005, 0x3ce); + outw(0x0e06, 0x3ce); + outw(0x0007, 0x3ce); + outw(0xff08, 0x3ce); + + /* Attribute registers */ + inb(0x3da); + outb(0x00, 0x3c0); + outb(0x00, 0x3c0); + + inb(0x3da); + outb(0x01, 0x3c0); + outb(0x01, 0x3c0); + + inb(0x3da); + outb(0x02, 0x3c0); + outb(0x02, 0x3c0); + + inb(0x3da); + outb(0x03, 0x3c0); + outb(0x03, 0x3c0); + + inb(0x3da); + outb(0x04, 0x3c0); + outb(0x04, 0x3c0); + + inb(0x3da); + outb(0x05, 0x3c0); + outb(0x05, 0x3c0); + + inb(0x3da); + outb(0x06, 0x3c0); + outb(0x14, 0x3c0); + + inb(0x3da); + outb(0x07, 0x3c0); + outb(0x07, 0x3c0); + + inb(0x3da); + outb(0x08, 0x3c0); + outb(0x38, 0x3c0); + + inb(0x3da); + outb(0x09, 0x3c0); + outb(0x39, 0x3c0); + + inb(0x3da); + outb(0x0a, 0x3c0); + outb(0x3a, 0x3c0); + + inb(0x3da); + outb(0x0b, 0x3c0); + outb(0x3b, 0x3c0); + + inb(0x3da); + outb(0x0c, 0x3c0); + outb(0x3c, 0x3c0); + + inb(0x3da); + outb(0x0d, 0x3c0); + outb(0x3d, 0x3c0); + + inb(0x3da); + outb(0x0e, 0x3c0); + outb(0x3e, 0x3c0); + + inb(0x3da); + outb(0x0f, 0x3c0); + outb(0x3f, 0x3c0); + + inb(0x3da); + outb(0x10, 0x3c0); + outb(0x0c, 0x3c0); + + inb(0x3da); + outb(0x11, 0x3c0); + outb(0x00, 0x3c0); + + inb(0x3da); + outb(0x12, 0x3c0); + outb(0x0f, 0x3c0); + + inb(0x3da); + outb(0x13, 0x3c0); + outb(0x08, 0x3c0); + + inb(0x3da); + outb(0x14, 0x3c0); + outb(0x00, 0x3c0); + + /* Goodbye */ + inb(0x3da); + outb(0x20, 0x3c0); +}