diff options
Diffstat (limited to 'arch/x86_64.c')
-rw-r--r-- | arch/x86_64.c | 907 |
1 files changed, 907 insertions, 0 deletions
diff --git a/arch/x86_64.c b/arch/x86_64.c new file mode 100644 index 0000000..2b3c0bb --- /dev/null +++ b/arch/x86_64.c @@ -0,0 +1,907 @@ +/* + * x86_64.c + * + * Copyright (C) 2006, 2007, 2008 NEC Corporation + * + * 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; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + */ +#ifdef __x86_64__ + +#include "../print_info.h" +#include "../elf_info.h" +#include "../makedumpfile.h" +extern struct vmap_pfns *gvmem_pfns; +extern int nr_gvmem_pfns; + +static unsigned long +get_xen_p2m_mfn(void) +{ + if (info->xen_crash_info_v >= 2) + return info->xen_crash_info.v2-> + dom0_pfn_to_mfn_frame_list_list; + if (info->xen_crash_info_v >= 1) + return info->xen_crash_info.v1-> + dom0_pfn_to_mfn_frame_list_list; + return NOT_FOUND_LONG_VALUE; +} + +static int +check_5level_paging(void) +{ + if (NUMBER(pgtable_l5_enabled) != NOT_FOUND_NUMBER && + NUMBER(pgtable_l5_enabled) != 0) + return TRUE; + else + return FALSE; +} + +unsigned long +get_kaslr_offset_x86_64(unsigned long vaddr) +{ + unsigned int i; + char buf[BUFSIZE_FGETS], *endp; + + if (!info->kaslr_offset && info->file_vmcoreinfo) { + if (fseek(info->file_vmcoreinfo, 0, SEEK_SET) < 0) { + ERRMSG("Can't seek the vmcoreinfo file(%s). %s\n", + info->name_vmcoreinfo, strerror(errno)); + return FALSE; + } + + while (fgets(buf, BUFSIZE_FGETS, info->file_vmcoreinfo)) { + i = strlen(buf); + if (!i) + break; + if (buf[i - 1] == '\n') + buf[i - 1] = '\0'; + if (strncmp(buf, STR_KERNELOFFSET, + strlen(STR_KERNELOFFSET)) == 0) + info->kaslr_offset = + strtoul(buf+strlen(STR_KERNELOFFSET),&endp,16); + } + } + if (vaddr >= __START_KERNEL_map && + vaddr < __START_KERNEL_map + info->kaslr_offset) + return info->kaslr_offset; + else + /* + * TODO: we need to check if it is vmalloc/vmmemmap/module + * address, we will have different offset + */ + return 0; +} + +static int +get_page_offset_x86_64(void) +{ + int i; + unsigned long long phys_start; + unsigned long long virt_start; + unsigned long page_offset_base; + + if (info->kaslr_offset && (info->fd_vmlinux != -1)) { + page_offset_base = get_symbol_addr("page_offset_base"); + page_offset_base += info->kaslr_offset; + if (!readmem(VADDR, page_offset_base, &info->page_offset, + sizeof(info->page_offset))) { + ERRMSG("Can't read page_offset_base.\n"); + return FALSE; + } + return TRUE; + } + + if (get_num_pt_loads()) { + for (i = 0; + get_pt_load(i, &phys_start, NULL, &virt_start, NULL); + i++) { + if (virt_start != NOT_KV_ADDR + && virt_start < __START_KERNEL_map + && phys_start != NOT_PADDR) { + info->page_offset = virt_start - phys_start; + return TRUE; + } + } + } + + if (info->kernel_version < KERNEL_VERSION(2, 6, 27)) { + info->page_offset = __PAGE_OFFSET_ORIG; + } else if(check_5level_paging()) { + info->page_offset = __PAGE_OFFSET_5LEVEL; + } else { + info->page_offset = __PAGE_OFFSET_2_6_27; + } + + return TRUE; +} + +int +get_phys_base_x86_64(void) +{ + int i; + unsigned long long phys_start; + unsigned long long virt_start; + + /* + * Get the relocatable offset + */ + info->phys_base = 0; /* default/traditional */ + if (NUMBER(phys_base) != NOT_FOUND_NUMBER) { + info->phys_base = NUMBER(phys_base); + return TRUE; + } + + /* linux-2.6.21 or older don't have phys_base, should be set to 0. */ + if (!has_vmcoreinfo()) { + SYMBOL_INIT(phys_base, "phys_base"); + if (SYMBOL(phys_base) == NOT_FOUND_SYMBOL) { + return TRUE; + } + } + + for (i = 0; get_pt_load(i, &phys_start, NULL, &virt_start, NULL); i++) { + if (virt_start >= __START_KERNEL_map + && phys_start != NOT_PADDR) { + + info->phys_base = phys_start - + (virt_start & ~(__START_KERNEL_map)); + + break; + } + } + + return TRUE; +} + +int +get_machdep_info_x86_64(void) +{ + unsigned long p2m_mfn; + int i, j, mfns[MAX_X86_64_FRAMES]; + unsigned long frame_mfn[MAX_X86_64_FRAMES]; + unsigned long buf[MFNS_PER_FRAME]; + + info->section_size_bits = _SECTION_SIZE_BITS; + + if (!is_xen_memory()) + return TRUE; + + /* + * Get the information for translating domain-0's physical + * address into machine address. + */ + p2m_mfn = get_xen_p2m_mfn(); + if (p2m_mfn == (unsigned long)NOT_FOUND_LONG_VALUE) { + ERRMSG("Can't get p2m_mfn address.\n"); + return FALSE; + } + if (!readmem(PADDR, pfn_to_paddr(p2m_mfn), + &frame_mfn, PAGESIZE())) { + ERRMSG("Can't read p2m_mfn.\n"); + return FALSE; + } + + /* + * Count the number of p2m frame. + */ + for (i = 0; i < MAX_X86_64_FRAMES; i++) { + mfns[i] = 0; + if (!frame_mfn[i]) + break; + + if (!readmem(PADDR, pfn_to_paddr(frame_mfn[i]), &buf, + PAGESIZE())) { + ERRMSG("Can't get frame_mfn[%d].\n", i); + return FALSE; + } + for (j = 0; j < MFNS_PER_FRAME; j++) { + if (!buf[j]) + break; + + mfns[i]++; + } + info->p2m_frames += mfns[i]; + } + info->p2m_mfn_frame_list + = malloc(sizeof(unsigned long) * info->p2m_frames); + if (info->p2m_mfn_frame_list == NULL) { + ERRMSG("Can't allocate memory for p2m_mfn_frame_list. %s\n", + strerror(errno)); + return FALSE; + } + + /* + * Get p2m_mfn_frame_list. + */ + for (i = 0; i < MAX_X86_64_FRAMES; i++) { + if (!frame_mfn[i]) + break; + + if (!readmem(PADDR, pfn_to_paddr(frame_mfn[i]), + &info->p2m_mfn_frame_list[i * MFNS_PER_FRAME], + mfns[i] * sizeof(unsigned long))) { + ERRMSG("Can't get p2m_mfn_frame_list.\n"); + return FALSE; + } + if (mfns[i] != MFNS_PER_FRAME) + break; + } + return TRUE; +} + +int +get_versiondep_info_x86_64(void) +{ + /* + * On linux-2.6.26, MAX_PHYSMEM_BITS is changed to 44 from 40. + */ + if (info->kernel_version < KERNEL_VERSION(2, 6, 26)) + info->max_physmem_bits = _MAX_PHYSMEM_BITS_ORIG; + else if (info->kernel_version < KERNEL_VERSION(2, 6, 31)) + info->max_physmem_bits = _MAX_PHYSMEM_BITS_2_6_26; + else if(check_5level_paging()) + info->max_physmem_bits = _MAX_PHYSMEM_BITS_5LEVEL; + else + info->max_physmem_bits = _MAX_PHYSMEM_BITS_2_6_31; + + if (!get_page_offset_x86_64()) + return FALSE; + + if (info->kernel_version < KERNEL_VERSION(2, 6, 31)) { + info->vmemmap_start = VMEMMAP_START_ORIG; + info->vmemmap_end = VMEMMAP_END_ORIG; + } else if(check_5level_paging()) { + info->vmemmap_start = VMEMMAP_START_5LEVEL; + info->vmemmap_end = VMEMMAP_END_5LEVEL; + } else { + info->vmemmap_start = VMEMMAP_START_2_6_31; + info->vmemmap_end = VMEMMAP_END_2_6_31; + } + + return TRUE; +} + +/* + * Translate a virtual address to a physical address by using 4 levels paging. + */ +unsigned long long +__vtop4_x86_64(unsigned long vaddr, unsigned long pagetable) +{ + unsigned long page_dir, pgd, pud_paddr, pud_pte, pmd_paddr, pmd_pte; + unsigned long pte_paddr, pte; + unsigned long p4d_paddr, p4d_pte; + + /* + * Get PGD. + */ + page_dir = pagetable; + if (is_xen_memory()) { + page_dir = ptom_xen(page_dir); + if (page_dir == NOT_PADDR) + return NOT_PADDR; + } + + if (check_5level_paging()) { + page_dir += pgd5_index(vaddr) * sizeof(unsigned long); + if (!readmem(PADDR, page_dir, &pgd, sizeof pgd)) { + ERRMSG("Can't get pgd (page_dir:%lx).\n", page_dir); + return NOT_PADDR; + } + if (info->vaddr_for_vtop == vaddr) + MSG(" PGD : %16lx => %16lx\n", page_dir, pgd); + + if (!(pgd & _PAGE_PRESENT)) { + ERRMSG("Can't get a valid pgd.\n"); + return NOT_PADDR; + } + /* + * Get P4D. + */ + p4d_paddr = pgd & ENTRY_MASK; + p4d_paddr += p4d_index(vaddr) * sizeof(unsigned long); + if (!readmem(PADDR, p4d_paddr, &p4d_pte, sizeof p4d_pte)) { + ERRMSG("Can't get p4d_pte (p4d_paddr:%lx).\n", p4d_paddr); + return NOT_PADDR; + } + if (info->vaddr_for_vtop == vaddr) + MSG(" P4D : %16lx => %16lx\n", p4d_paddr, p4d_pte); + + if (!(p4d_pte & _PAGE_PRESENT)) { + ERRMSG("Can't get a valid p4d_pte.\n"); + return NOT_PADDR; + } + pud_paddr = p4d_pte & ENTRY_MASK; + }else { + page_dir += pgd_index(vaddr) * sizeof(unsigned long); + if (!readmem(PADDR, page_dir, &pgd, sizeof pgd)) { + ERRMSG("Can't get pgd (page_dir:%lx).\n", page_dir); + return NOT_PADDR; + } + if (info->vaddr_for_vtop == vaddr) + MSG(" PGD : %16lx => %16lx\n", page_dir, pgd); + + if (!(pgd & _PAGE_PRESENT)) { + ERRMSG("Can't get a valid pgd.\n"); + return NOT_PADDR; + } + pud_paddr = pgd & ENTRY_MASK; + } + + /* + * Get PUD. + */ + pud_paddr += pud_index(vaddr) * sizeof(unsigned long); + if (!readmem(PADDR, pud_paddr, &pud_pte, sizeof pud_pte)) { + ERRMSG("Can't get pud_pte (pud_paddr:%lx).\n", pud_paddr); + return NOT_PADDR; + } + if (info->vaddr_for_vtop == vaddr) + MSG(" PUD : %16lx => %16lx\n", pud_paddr, pud_pte); + + if (!(pud_pte & _PAGE_PRESENT)) { + ERRMSG("Can't get a valid pud_pte.\n"); + return NOT_PADDR; + } + if (pud_pte & _PAGE_PSE) /* 1GB pages */ + return (pud_pte & ENTRY_MASK & PUD_MASK) + + (vaddr & ~PUD_MASK); + + /* + * Get PMD. + */ + pmd_paddr = pud_pte & ENTRY_MASK; + pmd_paddr += pmd_index(vaddr) * sizeof(unsigned long); + if (!readmem(PADDR, pmd_paddr, &pmd_pte, sizeof pmd_pte)) { + ERRMSG("Can't get pmd_pte (pmd_paddr:%lx).\n", pmd_paddr); + return NOT_PADDR; + } + if (info->vaddr_for_vtop == vaddr) + MSG(" PMD : %16lx => %16lx\n", pmd_paddr, pmd_pte); + + if (!(pmd_pte & _PAGE_PRESENT)) { + ERRMSG("Can't get a valid pmd_pte.\n"); + return NOT_PADDR; + } + if (pmd_pte & _PAGE_PSE) /* 2MB pages */ + return (pmd_pte & ENTRY_MASK & PMD_MASK) + + (vaddr & ~PMD_MASK); + + /* + * Get PTE. + */ + pte_paddr = pmd_pte & ENTRY_MASK; + pte_paddr += pte_index(vaddr) * sizeof(unsigned long); + if (!readmem(PADDR, pte_paddr, &pte, sizeof pte)) { + ERRMSG("Can't get pte (pte_paddr:%lx).\n", pte_paddr); + return NOT_PADDR; + } + if (info->vaddr_for_vtop == vaddr) + MSG(" PTE : %16lx => %16lx\n", pte_paddr, pte); + + if (!(pte & _PAGE_PRESENT)) { + ERRMSG("Can't get a valid pte.\n"); + return NOT_PADDR; + } + return (pte & ENTRY_MASK) + PAGEOFFSET(vaddr); +} + +unsigned long long +vtop4_x86_64(unsigned long vaddr) +{ + unsigned long pagetable; + unsigned long init_level4_pgt; + + if (SYMBOL(init_level4_pgt) != NOT_FOUND_SYMBOL) + init_level4_pgt = SYMBOL(init_level4_pgt); + else if (SYMBOL(init_top_pgt) != NOT_FOUND_SYMBOL) + init_level4_pgt = SYMBOL(init_top_pgt); + else { + ERRMSG("Can't get the symbol of init_level4_pgt/init_top_pgt.\n"); + return NOT_PADDR; + } + + pagetable = init_level4_pgt - __START_KERNEL_map + info->phys_base; + + return __vtop4_x86_64(vaddr, pagetable); +} + +unsigned long long +vtop4_x86_64_pagetable(unsigned long vaddr, unsigned long pagetable) +{ + return __vtop4_x86_64(vaddr, pagetable); +} + +/* + * for Xen extraction + */ +unsigned long long +kvtop_xen_x86_64(unsigned long kvaddr) +{ + unsigned long long dirp, entry; + + if (!is_xen_vaddr(kvaddr)) + return NOT_PADDR; + + if (is_xen_text(kvaddr)) + return (unsigned long)kvaddr - XEN_VIRT_START + info->xen_phys_start; + + if (is_direct(kvaddr)) + return (unsigned long)kvaddr - DIRECTMAP_VIRT_START; + + if ((dirp = kvtop_xen_x86_64(SYMBOL(pgd_l4))) == NOT_PADDR) + return NOT_PADDR; + + /* + * Get PGD. + */ + dirp += pgd_index(kvaddr) * sizeof(unsigned long long); + if (!readmem(PADDR, dirp, &entry, sizeof(entry))) + return NOT_PADDR; + + if (!(entry & _PAGE_PRESENT)) + return NOT_PADDR; + + /* + * Get PUD. + */ + dirp = entry & ENTRY_MASK; + dirp += pud_index(kvaddr) * sizeof(unsigned long long); + if (!readmem(PADDR, dirp, &entry, sizeof(entry))) + return NOT_PADDR; + + if (!(entry & _PAGE_PRESENT)) + return NOT_PADDR; + + if (entry & _PAGE_PSE) /* 1GB pages */ + return (entry & ENTRY_MASK & PUD_MASK) + + (kvaddr & ~PUD_MASK); + + /* + * Get PMD. + */ + dirp = entry & ENTRY_MASK; + dirp += pmd_index(kvaddr) * sizeof(unsigned long long); + if (!readmem(PADDR, dirp, &entry, sizeof(entry))) + return NOT_PADDR; + + if (!(entry & _PAGE_PRESENT)) + return NOT_PADDR; + + if (entry & _PAGE_PSE) /* 2MB pages */ + return (entry & ENTRY_MASK & PMD_MASK) + + (kvaddr & ~PMD_MASK); + + /* + * Get PTE. + */ + dirp = entry & ENTRY_MASK; + dirp += pte_index(kvaddr) * sizeof(unsigned long long); + if (!readmem(PADDR, dirp, &entry, sizeof(entry))) + return NOT_PADDR; + + if (!(entry & _PAGE_PRESENT)) { + return NOT_PADDR; + } + + return (entry & ENTRY_MASK) + PAGEOFFSET(kvaddr); +} + +int get_xen_basic_info_x86_64(void) +{ + if (!info->xen_phys_start) { + if (info->xen_crash_info_v < 2) { + ERRMSG("Can't get Xen physical start address.\n" + "Please use the --xen_phys_start option."); + return FALSE; + } + info->xen_phys_start = info->xen_crash_info.v2->xen_phys_start; + } + + info->xen_virt_start = SYMBOL(domain_list); + + /* + * Xen virtual mapping is aligned to 1 GiB boundary. + * domain_list lives in bss which sits no more than + * 1 GiB below beginning of virtual address space. + */ + info->xen_virt_start &= 0xffffffffc0000000; + + if (info->xen_crash_info.com && + info->xen_crash_info.com->xen_major_version >= 4) + info->directmap_virt_end = DIRECTMAP_VIRT_END_V4; + else + info->directmap_virt_end = DIRECTMAP_VIRT_END_V3; + + if (SYMBOL(pgd_l4) == NOT_FOUND_SYMBOL) { + ERRMSG("Can't get pml4.\n"); + return FALSE; + } + + if (SYMBOL(frame_table) != NOT_FOUND_SYMBOL) { + unsigned long frame_table_vaddr; + + if (!readmem(VADDR_XEN, SYMBOL(frame_table), + &frame_table_vaddr, sizeof(frame_table_vaddr))) { + ERRMSG("Can't get the value of frame_table.\n"); + return FALSE; + } + info->frame_table_vaddr = frame_table_vaddr; + } else { + if (info->xen_crash_info.com && + ((info->xen_crash_info.com->xen_major_version == 4 && + info->xen_crash_info.com->xen_minor_version >= 3) || + info->xen_crash_info.com->xen_major_version > 4)) + info->frame_table_vaddr = FRAMETABLE_VIRT_START_V4_3; + else + info->frame_table_vaddr = FRAMETABLE_VIRT_START_V3; + } + + if (!info->xen_crash_info.com || + info->xen_crash_info.com->xen_major_version < 4) { + unsigned long xen_end; + + if (SYMBOL(xenheap_phys_end) == NOT_FOUND_SYMBOL) { + ERRMSG("Can't get the symbol of xenheap_phys_end.\n"); + return FALSE; + } + if (!readmem(VADDR_XEN, SYMBOL(xenheap_phys_end), &xen_end, + sizeof(xen_end))) { + ERRMSG("Can't get the value of xenheap_phys_end.\n"); + return FALSE; + } + info->xen_heap_start = 0; + info->xen_heap_end = paddr_to_pfn(xen_end); + } + + return TRUE; +} + +int get_xen_info_x86_64(void) +{ + int i; + + if (info->xen_crash_info.com && + (info->xen_crash_info.com->xen_major_version >= 4 || + (info->xen_crash_info.com->xen_major_version == 3 && + info->xen_crash_info.com->xen_minor_version >= 4))) { + /* + * cf. changeset 0858f961c77a + */ + for (i = 0; i < info->num_domain; i++) { + info->domain_list[i].pickled_id = + (info->domain_list[i].domain_addr - + DIRECTMAP_VIRT_START) >> PAGESHIFT(); + } + } else { + /* + * pickled_id == domain addr for x86_64 + */ + for (i = 0; i < info->num_domain; i++) { + info->domain_list[i].pickled_id = + info->domain_list[i].domain_addr; + } + } + + return TRUE; +} + +/* + * Scan the kernel page table for the pfn's of the page structs + * Place them in array gvmem_pfns[nr_gvmem_pfns] + */ +int +find_vmemmap_x86_64() +{ + int i; + int pgd_index; + int start_range = 1; + int num_pmds=0, num_pmds_valid=0; + int break_in_valids, break_after_invalids; + int do_break; + int last_valid=0, last_invalid=0; + int pagestructsize, structsperhpage, hugepagesize; + long page_structs_per_pud; + long num_puds, groups = 0; + long pgdindex, pudindex, pmdindex; + long vaddr_base; + long rep_pfn_start = 0, rep_pfn_end = 0; + unsigned long init_level4_pgt; + unsigned long max_paddr, high_pfn; + unsigned long pgd_addr, pud_addr, pmd_addr; + unsigned long *pgdp, *pudp, *pmdp; + unsigned long pud_page[PTRS_PER_PUD]; + unsigned long pmd_page[PTRS_PER_PMD]; + unsigned long vmap_offset_start = 0, vmap_offset_end = 0; + unsigned long pmd, tpfn; + unsigned long pvaddr = 0; + unsigned long data_addr = 0, last_data_addr = 0, start_data_addr = 0; + /* + * data_addr is the paddr of the page holding the page structs. + * We keep lists of contiguous pages and the pfn's that their + * page structs represent. + * start_data_addr and last_data_addr mark start/end of those + * contiguous areas. + * An area descriptor is vmap start/end pfn and rep start/end + * of the pfn's represented by the vmap start/end. + */ + struct vmap_pfns *vmapp, *vmaphead = NULL, *cur, *tail; + + init_level4_pgt = SYMBOL(init_level4_pgt); + if (init_level4_pgt == NOT_FOUND_SYMBOL) + init_level4_pgt = SYMBOL(init_top_pgt); + + if (init_level4_pgt == NOT_FOUND_SYMBOL) { + ERRMSG("init_level4_pgt/init_top_pgt not found\n"); + return FAILED; + } + + pagestructsize = size_table.page; + hugepagesize = PTRS_PER_PMD * info->page_size; + vaddr_base = info->vmemmap_start; + max_paddr = get_max_paddr(); + /* + * the page structures are mapped at VMEMMAP_START (info->vmemmap_start) + * for max_paddr >> 12 page structures + */ + high_pfn = max_paddr >> 12; + pgd_index = pgd_index(vaddr_base); + pgd_addr = vaddr_to_paddr(init_level4_pgt); /* address of pgd */ + pgd_addr += pgd_index * sizeof(unsigned long); + page_structs_per_pud = (PTRS_PER_PUD * PTRS_PER_PMD * info->page_size) / + pagestructsize; + num_puds = (high_pfn + page_structs_per_pud - 1) / page_structs_per_pud; + pvaddr = VMEMMAP_START; + structsperhpage = hugepagesize / pagestructsize; + + /* outer loop is for pud entries in the pgd */ + for (pgdindex = 0, pgdp = (unsigned long *)pgd_addr; pgdindex < num_puds; + pgdindex++, pgdp++) { + + /* read the pgd one word at a time, into pud_addr */ + if (!readmem(PADDR, (unsigned long long)pgdp, (void *)&pud_addr, + sizeof(unsigned long))) { + ERRMSG("Can't get pgd entry for slot %d.\n", pgd_index); + return FAILED; + } + + /* mask the pgd entry for the address of the pud page */ + pud_addr &= PMASK; + if (pud_addr == 0) + continue; + /* read the entire pud page */ + if (!readmem(PADDR, (unsigned long long)pud_addr, (void *)pud_page, + PTRS_PER_PUD * sizeof(unsigned long))) { + ERRMSG("Can't get pud entry for pgd slot %ld.\n", pgdindex); + return FAILED; + } + /* step thru each pmd address in the pud page */ + /* pudp points to an entry in the pud page */ + for (pudp = (unsigned long *)pud_page, pudindex = 0; + pudindex < PTRS_PER_PUD; pudindex++, pudp++) { + pmd_addr = *pudp & PMASK; + /* read the entire pmd page */ + if (pmd_addr == 0) + continue; + if (!readmem(PADDR, pmd_addr, (void *)pmd_page, + PTRS_PER_PMD * sizeof(unsigned long))) { + ERRMSG("Can't get pud entry for slot %ld.\n", pudindex); + return FAILED; + } + /* pmdp points to an entry in the pmd */ + for (pmdp = (unsigned long *)pmd_page, pmdindex = 0; + pmdindex < PTRS_PER_PMD; pmdindex++, pmdp++) { + /* linear page position in this page table: */ + pmd = *pmdp; + num_pmds++; + tpfn = (pvaddr - VMEMMAP_START) / + pagestructsize; + if (tpfn >= high_pfn) { + break; + } + /* + * vmap_offset_start: + * Starting logical position in the + * vmemmap array for the group stays + * constant until a hole in the table + * or a break in contiguousness. + */ + + /* + * Ending logical position in the + * vmemmap array: + */ + vmap_offset_end += hugepagesize; + do_break = 0; + break_in_valids = 0; + break_after_invalids = 0; + /* + * We want breaks either when: + * - we hit a hole (invalid) + * - we discontiguous page is a string of valids + */ + if (pmd) { + data_addr = (pmd & PMASK); + if (start_range) { + /* first-time kludge */ + start_data_addr = data_addr; + last_data_addr = start_data_addr + - hugepagesize; + start_range = 0; + } + if (last_invalid) { + /* end of a hole */ + start_data_addr = data_addr; + last_data_addr = start_data_addr + - hugepagesize; + /* trigger update of offset */ + do_break = 1; + } + last_valid = 1; + last_invalid = 0; + /* + * we have a gap in physical + * contiguousness in the table. + */ + /* ?? consecutive holes will have + same data_addr */ + if (data_addr != + last_data_addr + hugepagesize) { + do_break = 1; + break_in_valids = 1; + } + DEBUG_MSG("valid: pud %ld pmd %ld pfn %#lx" + " pvaddr %#lx pfns %#lx-%lx" + " start %#lx end %#lx\n", + pudindex, pmdindex, + data_addr >> 12, + pvaddr, tpfn, + tpfn + structsperhpage - 1, + vmap_offset_start, + vmap_offset_end); + num_pmds_valid++; + if (!(pmd & _PAGE_PSE)) { + printf("vmemmap pmd not huge, abort\n"); + return FAILED; + } + } else { + if (last_valid) { + /* this a hole after some valids */ + do_break = 1; + break_in_valids = 1; + break_after_invalids = 0; + } + last_valid = 0; + last_invalid = 1; + /* + * There are holes in this sparsely + * populated table; they are 2MB gaps + * represented by null pmd entries. + */ + DEBUG_MSG("invalid: pud %ld pmd %ld %#lx" + " pfns %#lx-%lx start %#lx end" + " %#lx\n", pudindex, pmdindex, + pvaddr, tpfn, + tpfn + structsperhpage - 1, + vmap_offset_start, + vmap_offset_end); + } + if (do_break) { + /* The end of a hole is not summarized. + * It must be the start of a hole or + * hitting a discontiguous series. + */ + if (break_in_valids || break_after_invalids) { + /* + * calculate that pfns + * represented by the current + * offset in the vmemmap. + */ + /* page struct even partly on this page */ + rep_pfn_start = vmap_offset_start / + pagestructsize; + /* ending page struct entirely on + this page */ + rep_pfn_end = ((vmap_offset_end - + hugepagesize) / pagestructsize); + DEBUG_MSG("vmap pfns %#lx-%lx " + "represent pfns %#lx-%lx\n\n", + start_data_addr >> PAGESHIFT(), + last_data_addr >> PAGESHIFT(), + rep_pfn_start, rep_pfn_end); + groups++; + vmapp = (struct vmap_pfns *)malloc( + sizeof(struct vmap_pfns)); + /* pfn of this 2MB page of page structs */ + vmapp->vmap_pfn_start = start_data_addr + >> PTE_SHIFT; + vmapp->vmap_pfn_end = last_data_addr + >> PTE_SHIFT; + /* these (start/end) are literal pfns + * on this page, not start and end+1 */ + vmapp->rep_pfn_start = rep_pfn_start; + vmapp->rep_pfn_end = rep_pfn_end; + + if (!vmaphead) { + vmaphead = vmapp; + vmapp->next = vmapp; + vmapp->prev = vmapp; + } else { + tail = vmaphead->prev; + vmaphead->prev = vmapp; + tail->next = vmapp; + vmapp->next = vmaphead; + vmapp->prev = tail; + } + } + + /* update logical position at every break */ + vmap_offset_start = + vmap_offset_end - hugepagesize; + start_data_addr = data_addr; + } + + last_data_addr = data_addr; + pvaddr += hugepagesize; + /* + * pvaddr is current virtual address + * eg 0xffffea0004200000 if + * vmap_offset_start is 4200000 + */ + } + } + tpfn = (pvaddr - VMEMMAP_START) / pagestructsize; + if (tpfn >= high_pfn) { + break; + } + } + rep_pfn_start = vmap_offset_start / pagestructsize; + rep_pfn_end = (vmap_offset_end - hugepagesize) / pagestructsize; + DEBUG_MSG("vmap pfns %#lx-%lx represent pfns %#lx-%lx\n\n", + start_data_addr >> PAGESHIFT(), last_data_addr >> PAGESHIFT(), + rep_pfn_start, rep_pfn_end); + groups++; + vmapp = (struct vmap_pfns *)malloc(sizeof(struct vmap_pfns)); + vmapp->vmap_pfn_start = start_data_addr >> PTE_SHIFT; + vmapp->vmap_pfn_end = last_data_addr >> PTE_SHIFT; + vmapp->rep_pfn_start = rep_pfn_start; + vmapp->rep_pfn_end = rep_pfn_end; + if (!vmaphead) { + vmaphead = vmapp; + vmapp->next = vmapp; + vmapp->prev = vmapp; + } else { + tail = vmaphead->prev; + vmaphead->prev = vmapp; + tail->next = vmapp; + vmapp->next = vmaphead; + vmapp->prev = tail; + } + DEBUG_MSG("num_pmds: %d num_pmds_valid %d\n", num_pmds, num_pmds_valid); + + /* transfer the linked list to an array */ + cur = vmaphead; + gvmem_pfns = (struct vmap_pfns *)malloc(sizeof(struct vmap_pfns) * groups); + i = 0; + do { + vmapp = gvmem_pfns + i; + vmapp->vmap_pfn_start = cur->vmap_pfn_start; + vmapp->vmap_pfn_end = cur->vmap_pfn_end; + vmapp->rep_pfn_start = cur->rep_pfn_start; + vmapp->rep_pfn_end = cur->rep_pfn_end; + cur = cur->next; + free(cur->prev); + i++; + } while (cur != vmaphead); + nr_gvmem_pfns = i; + return COMPLETED; +} + +#endif /* x86_64 */ + |