diff options
Diffstat (limited to 'arch/arm64.c')
-rw-r--r-- | arch/arm64.c | 378 |
1 files changed, 378 insertions, 0 deletions
diff --git a/arch/arm64.c b/arch/arm64.c new file mode 100644 index 0000000..2fd3e18 --- /dev/null +++ b/arch/arm64.c @@ -0,0 +1,378 @@ +/* + * arch/arm64.c : Based on arch/arm.c + * + * Copyright (C) 2015 Red Hat, Pratyush Anand <panand@redhat.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. + */ + +#ifdef __aarch64__ + +#include "../elf_info.h" +#include "../makedumpfile.h" +#include "../print_info.h" + +typedef struct { + unsigned long pgd; +} pgd_t; + +typedef struct { + pgd_t pgd; +} pud_t; + +typedef struct { + pud_t pud; +} pmd_t; + +typedef struct { + unsigned long pte; +} pte_t; + +static int pgtable_level; +static int va_bits; +static unsigned long kimage_voffset; + +#define SZ_4K (4 * 1024) +#define SZ_16K (16 * 1024) +#define SZ_64K (64 * 1024) +#define SZ_128M (128 * 1024 * 1024) + +#define PAGE_OFFSET_36 ((0xffffffffffffffffUL) << 36) +#define PAGE_OFFSET_39 ((0xffffffffffffffffUL) << 39) +#define PAGE_OFFSET_42 ((0xffffffffffffffffUL) << 42) +#define PAGE_OFFSET_47 ((0xffffffffffffffffUL) << 47) +#define PAGE_OFFSET_48 ((0xffffffffffffffffUL) << 48) + +#define pgd_val(x) ((x).pgd) +#define pud_val(x) (pgd_val((x).pgd)) +#define pmd_val(x) (pud_val((x).pud)) +#define pte_val(x) ((x).pte) + +#define PAGE_MASK (~(PAGESIZE() - 1)) +#define PGDIR_SHIFT ((PAGESHIFT() - 3) * pgtable_level + 3) +#define PTRS_PER_PGD (1 << (va_bits - PGDIR_SHIFT)) +#define PUD_SHIFT get_pud_shift_arm64() +#define PUD_SIZE (1UL << PUD_SHIFT) +#define PUD_MASK (~(PUD_SIZE - 1)) +#define PTRS_PER_PTE (1 << (PAGESHIFT() - 3)) +#define PTRS_PER_PUD PTRS_PER_PTE +#define PMD_SHIFT ((PAGESHIFT() - 3) * 2 + 3) +#define PMD_SIZE (1UL << PMD_SHIFT) +#define PMD_MASK (~(PMD_SIZE - 1)) +#define PTRS_PER_PMD PTRS_PER_PTE + +#define PAGE_PRESENT (1 << 0) +#define SECTIONS_SIZE_BITS 30 +/* Highest possible physical address supported */ +#define PHYS_MASK_SHIFT 48 +#define PHYS_MASK ((1UL << PHYS_MASK_SHIFT) - 1) +/* + * Remove the highest order bits that are not a part of the + * physical address in a section + */ +#define PMD_SECTION_MASK ((1UL << 40) - 1) + +#define PMD_TYPE_MASK 3 +#define PMD_TYPE_SECT 1 +#define PMD_TYPE_TABLE 3 + +#define PUD_TYPE_MASK 3 +#define PUD_TYPE_SECT 1 +#define PUD_TYPE_TABLE 3 + +#define pgd_index(vaddr) (((vaddr) >> PGDIR_SHIFT) & (PTRS_PER_PGD - 1)) +#define pgd_offset(pgdir, vaddr) ((pgd_t *)(pgdir) + pgd_index(vaddr)) + +#define pte_index(vaddr) (((vaddr) >> PAGESHIFT()) & (PTRS_PER_PTE - 1)) +#define pmd_page_paddr(pmd) (pmd_val(pmd) & PHYS_MASK & (int32_t)PAGE_MASK) +#define pte_offset(dir, vaddr) ((pte_t*)pmd_page_paddr((*dir)) + pte_index(vaddr)) + +#define pmd_index(vaddr) (((vaddr) >> PMD_SHIFT) & (PTRS_PER_PMD - 1)) +#define pud_page_paddr(pud) (pud_val(pud) & PHYS_MASK & (int32_t)PAGE_MASK) +#define pmd_offset_pgtbl_lvl_2(pud, vaddr) ((pmd_t *)pud) +#define pmd_offset_pgtbl_lvl_3(pud, vaddr) ((pmd_t *)pud_page_paddr((*pud)) + pmd_index(vaddr)) + +#define pud_index(vaddr) (((vaddr) >> PUD_SHIFT) & (PTRS_PER_PUD - 1)) +#define pgd_page_paddr(pgd) (pgd_val(pgd) & PHYS_MASK & (int32_t)PAGE_MASK) + +static unsigned long long +__pa(unsigned long vaddr) +{ + if (kimage_voffset == NOT_FOUND_NUMBER || + (vaddr >= PAGE_OFFSET)) + return (vaddr - PAGE_OFFSET + info->phys_base); + else + return (vaddr - kimage_voffset); +} + +static int +get_pud_shift_arm64(void) +{ + if (pgtable_level == 4) + return ((PAGESHIFT() - 3) * 3 + 3); + else + return PGDIR_SHIFT; +} + +static pmd_t * +pmd_offset(pud_t *puda, pud_t *pudv, unsigned long vaddr) +{ + if (pgtable_level == 2) { + return pmd_offset_pgtbl_lvl_2(puda, vaddr); + } else { + return pmd_offset_pgtbl_lvl_3(pudv, vaddr); + } +} + +static pud_t * +pud_offset(pgd_t *pgda, pgd_t *pgdv, unsigned long vaddr) +{ + if (pgtable_level == 4) + return ((pud_t *)pgd_page_paddr((*pgdv)) + pud_index(vaddr)); + else + return (pud_t *)(pgda); +} + +static int calculate_plat_config(void) +{ + /* derive pgtable_level as per arch/arm64/Kconfig */ + if ((PAGESIZE() == SZ_16K && va_bits == 36) || + (PAGESIZE() == SZ_64K && va_bits == 42)) { + pgtable_level = 2; + } else if ((PAGESIZE() == SZ_64K && va_bits == 48) || + (PAGESIZE() == SZ_4K && va_bits == 39) || + (PAGESIZE() == SZ_16K && va_bits == 47)) { + pgtable_level = 3; + } else if ((PAGESIZE() != SZ_64K && va_bits == 48)) { + pgtable_level = 4; + } else { + ERRMSG("PAGE SIZE %#lx and VA Bits %d not supported\n", + PAGESIZE(), va_bits); + return FALSE; + } + + return TRUE; +} + +unsigned long +get_kvbase_arm64(void) +{ + return (0xffffffffffffffffUL << va_bits); +} + +int +get_phys_base_arm64(void) +{ + info->phys_base = NUMBER(PHYS_OFFSET); + + DEBUG_MSG("phys_base : %lx\n", info->phys_base); + + return TRUE; +} + +ulong +get_stext_symbol(void) +{ + int found; + FILE *fp; + char buf[BUFSIZE]; + char *kallsyms[MAXARGS]; + ulong kallsym; + + if (!file_exists("/proc/kallsyms")) { + ERRMSG("(%s) does not exist, will not be able to read symbols. %s\n", + "/proc/kallsyms", strerror(errno)); + return FALSE; + } + + if ((fp = fopen("/proc/kallsyms", "r")) == NULL) { + ERRMSG("Cannot open (%s) to read symbols. %s\n", + "/proc/kallsyms", strerror(errno)); + return FALSE; + } + + found = FALSE; + kallsym = 0; + + while (!found && fgets(buf, BUFSIZE, fp) && + (parse_line(buf, kallsyms) == 3)) { + if (hexadecimal(kallsyms[0], 0) && + STREQ(kallsyms[2], "_stext")) { + kallsym = htol(kallsyms[0], 0); + found = TRUE; + break; + } + } + fclose(fp); + + return(found ? kallsym : FALSE); +} + +int +get_machdep_info_arm64(void) +{ + /* Check if va_bits is still not initialized. If still 0, call + * get_versiondep_info() to initialize the same. + */ + if (!va_bits) + get_versiondep_info_arm64(); + + if (!calculate_plat_config()) { + ERRMSG("Can't determine platform config values\n"); + return FALSE; + } + + kimage_voffset = NUMBER(kimage_voffset); + info->max_physmem_bits = PHYS_MASK_SHIFT; + info->section_size_bits = SECTIONS_SIZE_BITS; + + DEBUG_MSG("kimage_voffset : %lx\n", kimage_voffset); + DEBUG_MSG("max_physmem_bits : %lx\n", info->max_physmem_bits); + DEBUG_MSG("section_size_bits: %lx\n", info->section_size_bits); + + return TRUE; +} + +unsigned long long +kvtop_xen_arm64(unsigned long kvaddr) +{ + return ERROR; +} + +int +get_xen_basic_info_arm64(void) +{ + return ERROR; +} + +int +get_xen_info_arm64(void) +{ + return ERROR; +} + +int +get_versiondep_info_arm64(void) +{ + ulong _stext; + + _stext = get_stext_symbol(); + if (!_stext) { + ERRMSG("Can't get the symbol of _stext.\n"); + return FALSE; + } + + /* Derive va_bits as per arch/arm64/Kconfig */ + if ((_stext & PAGE_OFFSET_36) == PAGE_OFFSET_36) { + va_bits = 36; + } else if ((_stext & PAGE_OFFSET_39) == PAGE_OFFSET_39) { + va_bits = 39; + } else if ((_stext & PAGE_OFFSET_42) == PAGE_OFFSET_42) { + va_bits = 42; + } else if ((_stext & PAGE_OFFSET_47) == PAGE_OFFSET_47) { + va_bits = 47; + } else if ((_stext & PAGE_OFFSET_48) == PAGE_OFFSET_48) { + va_bits = 48; + } else { + ERRMSG("Cannot find a proper _stext for calculating VA_BITS\n"); + return FALSE; + } + + info->page_offset = (0xffffffffffffffffUL) << (va_bits - 1); + + DEBUG_MSG("page_offset=%lx, va_bits=%d\n", info->page_offset, + va_bits); + + return TRUE; +} + +/* + * vaddr_to_paddr_arm64() - translate arbitrary virtual address to physical + * @vaddr: virtual address to translate + * + * Function translates @vaddr into physical address using page tables. This + * address can be any virtual address. Returns physical address of the + * corresponding virtual address or %NOT_PADDR when there is no translation. + */ +unsigned long long +vaddr_to_paddr_arm64(unsigned long vaddr) +{ + unsigned long long paddr = NOT_PADDR; + unsigned long long swapper_phys; + pgd_t *pgda, pgdv; + pud_t *puda, pudv; + pmd_t *pmda, pmdv; + pte_t *ptea, ptev; + + if (SYMBOL(swapper_pg_dir) == NOT_FOUND_SYMBOL) { + ERRMSG("Can't get the symbol of swapper_pg_dir.\n"); + return NOT_PADDR; + } + + swapper_phys = __pa(SYMBOL(swapper_pg_dir)); + + pgda = pgd_offset(swapper_phys, vaddr); + if (!readmem(PADDR, (unsigned long long)pgda, &pgdv, sizeof(pgdv))) { + ERRMSG("Can't read pgd\n"); + return NOT_PADDR; + } + + puda = pud_offset(pgda, &pgdv, vaddr); + if (!readmem(PADDR, (unsigned long long)puda, &pudv, sizeof(pudv))) { + ERRMSG("Can't read pud\n"); + return NOT_PADDR; + } + + if ((pud_val(pudv) & PUD_TYPE_MASK) == PUD_TYPE_SECT) { + /* 1GB section for Page Table level = 4 and Page Size = 4KB */ + paddr = (pud_val(pudv) & (PUD_MASK & PMD_SECTION_MASK)) + + (vaddr & (PUD_SIZE - 1)); + return paddr; + } + + pmda = pmd_offset(puda, &pudv, vaddr); + if (!readmem(PADDR, (unsigned long long)pmda, &pmdv, sizeof(pmdv))) { + ERRMSG("Can't read pmd\n"); + return NOT_PADDR; + } + + switch (pmd_val(pmdv) & PMD_TYPE_MASK) { + case PMD_TYPE_TABLE: + ptea = pte_offset(&pmdv, vaddr); + /* 64k page */ + if (!readmem(PADDR, (unsigned long long)ptea, &ptev, sizeof(ptev))) { + ERRMSG("Can't read pte\n"); + return NOT_PADDR; + } + + if (!(pte_val(ptev) & PAGE_PRESENT)) { + ERRMSG("Can't get a valid pte.\n"); + return NOT_PADDR; + } else { + + paddr = (PAGEBASE(pte_val(ptev)) & PHYS_MASK) + + (vaddr & (PAGESIZE() - 1)); + } + break; + case PMD_TYPE_SECT: + /* 512MB section for Page Table level = 3 and Page Size = 64KB*/ + paddr = (pmd_val(pmdv) & (PMD_MASK & PMD_SECTION_MASK)) + + (vaddr & (PMD_SIZE - 1)); + break; + } + + return paddr; +} + +#endif /* __aarch64__ */ |