/* * We're called here very early in the boot. * * Note that the kernel may be running at an address which is different * from the address that it was linked at, so we must use RELOC/PTRRELOC * to access static data (including strings). -- paulus */ notrace unsigned long __init early_init(unsigned long dt_ptr) { unsigned long offset = reloc_offset(); /* First zero the BSS -- use memset_io, some platforms don't have * caches on yet */ memset_io((void __iomem *)PTRRELOC(&__bss_start), 0, __bss_stop - __bss_start); /* * Identify the CPU type and fix up code sections * that depend on which cpu we have. */ identify_cpu(offset, mfspr(SPRN_PVR)); apply_feature_fixups(); return KERNELBASE + offset; }
/* This routine called with relocation disabled. */ void lmb_init(void) { u64 offset = reloc_offset(); struct lmb *_lmb = PTRRELOC(&lmb); memset(_lmb,0,sizeof(struct lmb)); /* Create a dummy zero size LMB which will get coalesced away later. * This simplifies the lmb_add() code below... */ _lmb->memory.region[0].base = 0; _lmb->memory.region[0].size = 0; _lmb->memory.cnt = 1; /* Ditto. */ _lmb->reserved.region[0].base = 0; _lmb->reserved.region[0].size = 0; _lmb->reserved.cnt = 1; }
u64 lmb_abs_to_phys(u64 aa) { u64 i, pa = aa; u64 offset = reloc_offset(); struct lmb *_lmb = PTRRELOC(&lmb); struct lmb_region *_mem = &(_lmb->memory); for (i=0; i < _mem->cnt; i++) { u64 lmbbase = _mem->region[i].base; u64 lmbsize = _mem->region[i].size; if ( lmb_addrs_overlap(aa,1,lmbbase,lmbsize) ) { pa = _mem->region[i].physbase + (aa - lmbbase); break; } } return pa; }
void phys_call_rtas(int token, int nargs, int nret, ...) { va_list list; unsigned long offset = reloc_offset(); struct rtas_args *rtas = PTRRELOC(&(get_paca()->xRtas)); int i; rtas->token = token; rtas->nargs = nargs; rtas->nret = nret; rtas->rets = (rtas_arg_t *)PTRRELOC(&(rtas->args[nargs])); va_start(list, nret); for (i = 0; i < nargs; i++) rtas->args[i] = (rtas_arg_t)LONG_LSW(va_arg(list, ulong)); va_end(list); enter_rtas(rtas); }
u64 lmb_alloc_base(u64 size, u64 align, u64 max_addr) { long i, j; u64 base = 0; u64 offset = reloc_offset(); struct lmb *_lmb = PTRRELOC(&lmb); struct lmb_region *_mem = &(_lmb->memory); struct lmb_region *_rsv = &(_lmb->reserved); for (i=_mem->cnt-1; i >= 0; i--) { u64 lmbbase = _mem->region[i].base; u64 lmbsize = _mem->region[i].size; if ( max_addr == LMB_ALLOC_ANYWHERE ) base = _ALIGN_DOWN(lmbbase+lmbsize-size, align); else if ( lmbbase < max_addr ) base = _ALIGN_DOWN(min(lmbbase+lmbsize,max_addr)-size, align); else continue; while ( (lmbbase <= base) && ((j = lmb_overlaps_region(_rsv,base,size)) >= 0) ) { base = _ALIGN_DOWN(_rsv->region[j].base-size, align); } if ( (base != 0) && (lmbbase <= base) ) break; } if ( i < 0 ) return 0; lmb_add_region(_rsv, base, size); return base; }
static int te_relocate(uintptr_t new_addr, void *te) { EFI_TE_IMAGE_HEADER *teih; EFI_IMAGE_DATA_DIRECTORY *relocd; EFI_IMAGE_BASE_RELOCATION *relocb; uintptr_t image_base; size_t fixup_offset; size_t num_relocs; uint16_t *reloc; size_t relocd_offset; uint8_t *te_base; uint32_t adj; teih = te; if (read_le16(&teih->Signature) != EFI_TE_IMAGE_HEADER_SIGNATURE) { printk(BIOS_ERR, "TE Signature mismatch: %x vs %x\n", read_le16(&teih->Signature), EFI_TE_IMAGE_HEADER_SIGNATURE); return -1; } /* * A TE image is created by converting a PE file. Because of this * the offsets within the headers are off. In order to calculate * the correct releative offets one needs to subtract fixup_offset * from the encoded offets. Similarly, the linked address of the * program is found by adding the fixup_offset to the ImageBase. */ fixup_offset = read_le16(&teih->StrippedSize); fixup_offset -= sizeof(EFI_TE_IMAGE_HEADER); /* Keep track of a base that is correctly adjusted so that offsets * can be used directly. */ te_base = te; te_base -= fixup_offset; image_base = read_le64(&teih->ImageBase); adj = new_addr - (image_base + fixup_offset); printk(FSP_DBG_LVL, "TE Image %p -> %p adjust value: %x\n", (void *)image_base, (void *)new_addr, adj); /* Adjust ImageBase for consistency. */ write_le64(&teih->ImageBase, (uint32_t)(image_base + adj)); relocd = &teih->DataDirectory[EFI_TE_IMAGE_DIRECTORY_ENTRY_BASERELOC]; relocd_offset = 0; /* Though the field name is VirtualAddress it's actually relative to * the beginning of the image which is linked at ImageBase. */ relocb = relative_offset(te, read_le32(&relocd->VirtualAddress) - fixup_offset); while (relocd_offset < read_le32(&relocd->Size)) { size_t rva_offset = read_le32(&relocb->VirtualAddress); printk(FSP_DBG_LVL, "Relocs for RVA offset %zx\n", rva_offset); num_relocs = read_le32(&relocb->SizeOfBlock) - sizeof(*relocb); num_relocs /= sizeof(uint16_t); reloc = relative_offset(relocb, sizeof(*relocb)); printk(FSP_DBG_LVL, "Num relocs in block: %zx\n", num_relocs); while (num_relocs > 0) { uint16_t reloc_val = read_le16(reloc); int type = reloc_type(reloc_val); size_t offset = reloc_offset(reloc_val); printk(FSP_DBG_LVL, "reloc type %x offset %zx\n", type, offset); if (type == EFI_IMAGE_REL_BASED_HIGHLOW) { uint32_t *reloc_addr; uint32_t val; offset += rva_offset; reloc_addr = (void *)&te_base[offset]; val = read_le32(reloc_addr); printk(FSP_DBG_LVL, "Adjusting %p %x -> %x\n", reloc_addr, val, val + adj); write_le32(reloc_addr, val + adj); } else if (type != EFI_IMAGE_REL_BASED_ABSOLUTE) { printk(BIOS_ERR, "Unknown reloc type: %x\n", type); return -1; } num_relocs--; reloc++; } /* Track consumption of relocation directory contents. */ relocd_offset += read_le32(&relocb->SizeOfBlock); /* Get next relocation block to process. */ relocb = relative_offset(relocb, read_le32(&relocb->SizeOfBlock)); } return 0; }