/* * 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" #define PAGE_OFFSET 0xe000000000000000UL #define EFI_PAGE_SHIFT 12 #define EFI_PAGE_SIZE (1UL<efi_memmap_base; void *src = (void *)boot_param->efi_memmap; uint64_t orig_type; efi_memory_desc_t *src_md, *dst_md; void *src_end = src + boot_param->efi_memmap_size; unsigned long i; for (; src < src_end; src += boot_param->efi_memdesc_size, dest += boot_param->efi_memdesc_size) { unsigned long mstart, mend; src_md = src; dst_md = dest; if (src_md->num_pages == 0) continue; mstart = src_md->phys_addr; mend = src_md->phys_addr + (src_md->num_pages << EFI_PAGE_SHIFT); *dst_md = *src_md; if (src_md->type == EFI_LOADER_DATA) dst_md->type = EFI_CONVENTIONAL_MEMORY; /* segments are already sorted and aligned to 4K */ orig_type = dst_md->type; for (i = 0; i < params->loaded_segments_num; i++) { struct loaded_segment *seg; unsigned long start_pages, mid_pages, end_pages; seg = ¶ms->loaded_segments[i]; if (seg->start < mstart || seg->start >= mend) continue; while (seg->end > mend && src < src_end) { src += boot_param->efi_memdesc_size; src_md = src; /* TODO check contig and attribute here */ mend = src_md->phys_addr + (src_md->num_pages << EFI_PAGE_SHIFT); } if (seg->end < mend && src < src_end) { void *src_next; efi_memory_desc_t *src_next_md; src_next = src + boot_param->efi_memdesc_size; src_next_md = src_next; if (src_next_md->type == EFI_CONVENTIONAL_MEMORY) { /* TODO check contig and attribute */ src += boot_param->efi_memdesc_size; src_md = src; mend = src_md->phys_addr + (src_md->num_pages << EFI_PAGE_SHIFT); } } 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) { dst_md->num_pages = start_pages; dest += boot_param->efi_memdesc_size; dst_md = dest; *dst_md = *src_md; } dst_md->phys_addr = seg->start; dst_md->num_pages = mid_pages; dst_md->type = EFI_LOADER_DATA; if (!end_pages) break; dest += boot_param->efi_memdesc_size; dst_md = dest; *dst_md = *src_md; dst_md->phys_addr = seg->end; dst_md->num_pages = end_pages; dst_md->type = orig_type; mstart = seg->end; } } boot_param->efi_memmap_size = dest - (void *)params->efi_memmap_base; } void flush_icache_range(char *start, unsigned long len) { unsigned long i, addr; addr = (unsigned long)start & ~31UL; len += (unsigned long)start - addr; 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; struct ia64_boot_param *new_boot_param = (struct ia64_boot_param *) params->boot_param_base; memcpy(new_boot_param, boot_param, 4096); /* * patch efi_runtime->set_virtual_address_map to a dummy function * * The EFI specification mandates that set_virtual_address_map only * takes effect the first time that it is called, and that * subsequent calls will return error. By replacing it with a * dummy function the new OS can think it is calling it again * without either the OS or any buggy EFI implementations getting * upset. * * Note: as the EFI specification says that set_virtual_address_map * will only take affect the first time it is called, the mapping * can't be updated, and thus mapping of the old and new OS really * needs to be the same. */ len = __dummy_efi_function_end - __dummy_efi_function; memcpy(command_line + command_line_len, __dummy_efi_function, len); systab = (efi_system_table_t *)new_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, new_boot_param); new_boot_param->efi_memmap = params->efi_memmap_base; new_boot_param->command_line = params->command_line; new_boot_param->console_info.orig_x = 0; new_boot_param->console_info.orig_y = 0; new_boot_param->initrd_start = params->ramdisk_base; new_boot_param->initrd_size = params->ramdisk_size; new_boot_param->vmcode_start = params->vmcode_base; new_boot_param->vmcode_size = params->vmcode_size; } /* This function can be used to execute after the SHA256 verification. */ void post_verification_setup_arch(void) { /* Nothing for now */ }