diff options
author | marcel <marcel@FreeBSD.org> | 2012-07-07 00:25:17 +0000 |
---|---|---|
committer | marcel <marcel@FreeBSD.org> | 2012-07-07 00:25:17 +0000 |
commit | c55246a63a6df83b0d22260f7e045c8d90ce40b2 (patch) | |
tree | 01586c6653c0e2a8b2640228e792f9511709fe0f /sys/ia64 | |
parent | e91d8ed66961ddedc1faefbdbd5e9aaf203640bc (diff) | |
download | FreeBSD-src-c55246a63a6df83b0d22260f7e045c8d90ce40b2.zip FreeBSD-src-c55246a63a6df83b0d22260f7e045c8d90ce40b2.tar.gz |
Hide the creation of phys_avail behind an API to make it easier to do it
correctly. We now iterate the EFI memory descriptors once and collect all
the information in a single pass. This includes:
1. The I/O port base address,
2. The PAL memory region. Have the physmem API track this.
3. Memory descriptors of memory we can't use, like bad memory, runtime
services code & data, etc. Have the physmem API track these.
4. memory descriptors of memory we can use or re-use, such as free
memory, boot time services code & data, loader code & data, etc.
These are added by the physmem API.
Since the PBVM page table and pages are in memory described as loader
data, inform the physmem API of chunks that need to be delated from the
available physical memory.
While here, remove Maxmem and replace it with the better named paddr_max.
Maxmem was defined as physmem, which is generally wrong. Now, paddr_max
is properly defined as the largesty physical address.
The upshot of all this is that:
1. We properly determine realmem.
2. We maximize physmem by re-using memory where possible.
3. We remove complexity from ia64_init() in machdep.c.
4. Remove confusion about realmem, physmem & Maxmem.
The new ia64_physmem_alloc() is to replace pmap_steal_memory() in pmap.c,
as well as replace the handcrafted allocation of the VHPT for the BSP in
pmap_bootstrap() in pmap.c. This is step 2 and addresses the manipulation
of phys_avail after it is being created.
Diffstat (limited to 'sys/ia64')
-rw-r--r-- | sys/ia64/ia64/busdma_machdep.c | 12 | ||||
-rw-r--r-- | sys/ia64/ia64/machdep.c | 203 | ||||
-rw-r--r-- | sys/ia64/ia64/physmem.c | 195 | ||||
-rw-r--r-- | sys/ia64/include/md_var.h | 9 | ||||
-rw-r--r-- | sys/ia64/include/param.h | 3 |
5 files changed, 269 insertions, 153 deletions
diff --git a/sys/ia64/ia64/busdma_machdep.c b/sys/ia64/ia64/busdma_machdep.c index d504e89..eaaf03f 100644 --- a/sys/ia64/ia64/busdma_machdep.c +++ b/sys/ia64/ia64/busdma_machdep.c @@ -262,7 +262,7 @@ bus_dma_tag_create(bus_dma_tag_t parent, bus_size_t alignment, atomic_add_int(&parent->ref_count, 1); } - if (newtag->lowaddr < ptoa(Maxmem) && (flags & BUS_DMA_ALLOCNOW) != 0) { + if (newtag->lowaddr < paddr_max && (flags & BUS_DMA_ALLOCNOW) != 0) { /* Must bounce */ if (ptoa(total_bpages) < maxsize) { @@ -340,7 +340,7 @@ bus_dmamap_create(bus_dma_tag_t dmat, int flags, bus_dmamap_t *mapp) * exclusion region, a data alignment that is stricter than 1, and/or * an active address boundary. */ - if (dmat->lowaddr < ptoa(Maxmem)) { + if (dmat->lowaddr < paddr_max) { /* Must bounce */ int maxpages; @@ -356,7 +356,7 @@ bus_dmamap_create(bus_dma_tag_t dmat, int flags, bus_dmamap_t *mapp) * Attempt to add pages to our pool on a per-instance * basis up to a sane limit. */ - maxpages = MIN(MAX_BPAGES, Maxmem - atop(dmat->lowaddr)); + maxpages = MIN(MAX_BPAGES, atop(paddr_max - dmat->lowaddr)); if ((dmat->flags & BUS_DMA_MIN_ALLOC_COMP) == 0 || (dmat->map_count > 0 && total_bpages < maxpages)) { int pages; @@ -438,7 +438,7 @@ bus_dmamem_alloc(bus_dma_tag_t dmat, void** vaddr, int flags, */ if ((dmat->maxsize <= PAGE_SIZE) && (dmat->alignment < dmat->maxsize) && - dmat->lowaddr >= ptoa(Maxmem)) { + dmat->lowaddr >= paddr_max) { *vaddr = malloc(dmat->maxsize, M_DEVBUF, mflags); } else { /* @@ -473,7 +473,7 @@ bus_dmamem_free(bus_dma_tag_t dmat, void *vaddr, bus_dmamap_t map) panic("bus_dmamem_free: Invalid map freed\n"); if ((dmat->maxsize <= PAGE_SIZE) && (dmat->alignment < dmat->maxsize) && - dmat->lowaddr >= ptoa(Maxmem)) + dmat->lowaddr >= paddr_max) free(vaddr, M_DEVBUF); else { contigfree(vaddr, dmat->maxsize, M_DEVBUF); @@ -506,7 +506,7 @@ _bus_dmamap_load_buffer(bus_dma_tag_t dmat, bus_dmamap_t map, void *buf, else pmap = NULL; - if ((dmat->lowaddr < ptoa(Maxmem) || dmat->boundary > 0 || + if ((dmat->lowaddr < paddr_max || dmat->boundary > 0 || dmat->alignment > 1) && map != &nobounce_dmamap && map->pagesneeded == 0) { vm_offset_t vendaddr; diff --git a/sys/ia64/ia64/machdep.c b/sys/ia64/ia64/machdep.c index 26fb525..e0c924a 100644 --- a/sys/ia64/ia64/machdep.c +++ b/sys/ia64/ia64/machdep.c @@ -152,22 +152,11 @@ SYSCTL_STRING(_hw, OID_AUTO, family, CTLFLAG_RD, cpu_family, 0, extern vm_offset_t ksym_start, ksym_end; #endif - struct msgbuf *msgbufp = NULL; /* Other subsystems (e.g., ACPI) can hook this later. */ void (*cpu_idle_hook)(void) = NULL; -long Maxmem = 0; -long realmem = 0; - -#define PHYSMAP_SIZE (2 * VM_PHYSSEG_MAX) - -vm_paddr_t phys_avail[PHYSMAP_SIZE + 2]; - -/* must be 2 less so 0 0 can signal end of chunks */ -#define PHYS_AVAIL_ARRAY_END ((sizeof(phys_avail) / sizeof(vm_offset_t)) - 2) - struct kva_md_info kmi; #define Mhz 1000000L @@ -270,25 +259,8 @@ cpu_startup(void *dummy) #ifdef PERFMON perfmon_init(); #endif - printf("real memory = %ld (%ld MB)\n", ia64_ptob(Maxmem), - ia64_ptob(Maxmem) / 1048576); - realmem = Maxmem; - - /* - * Display any holes after the first chunk of extended memory. - */ - if (bootverbose) { - int indx; - - printf("Physical memory chunk(s):\n"); - for (indx = 0; phys_avail[indx + 1] != 0; indx += 2) { - long size1 = phys_avail[indx + 1] - phys_avail[indx]; - - printf("0x%08lx - 0x%08lx, %ld bytes (%ld pages)\n", - phys_avail[indx], phys_avail[indx + 1] - 1, size1, - size1 >> PAGE_SHIFT); - } - } + printf("real memory = %ld (%ld MB)\n", ptoa(realmem), + ptoa(realmem) / 1048576); vm_ksubmap_init(&kmi); @@ -700,43 +672,87 @@ struct ia64_init_return ia64_init(void) { struct ia64_init_return ret; - int phys_avail_cnt; - vm_offset_t kernstart, kernend; - vm_offset_t kernstartpfn, kernendpfn, pfn0, pfn1; - char *p; struct efi_md *md; + pt_entry_t *pbvm_pgtbl_ent, *pbvm_pgtbl_lim; + char *p; + vm_offset_t kernend; + vm_size_t mdlen; int metadata_missing; - /* NO OUTPUT ALLOWED UNTIL FURTHER NOTICE */ - /* - * TODO: Disable interrupts, floating point etc. - * Maybe flush cache and tlb + * NO OUTPUT ALLOWED UNTIL FURTHER NOTICE. */ + ia64_set_fpsr(IA64_FPSR_DEFAULT); /* - * TODO: Get critical system information (if possible, from the - * information provided by the boot program). + * Region 6 is direct mapped UC and region 7 is direct mapped + * WC. The details of this is controlled by the Alt {I,D}TLB + * handlers. Here we just make sure that they have the largest + * possible page size to minimise TLB usage. */ + ia64_set_rr(IA64_RR_BASE(6), (6 << 8) | (PAGE_SHIFT << 2)); + ia64_set_rr(IA64_RR_BASE(7), (7 << 8) | (PAGE_SHIFT << 2)); + ia64_srlz_d(); + + /* Initialize/setup physical memory datastructures */ + ia64_physmem_init(); /* - * Look for the I/O ports first - we need them for console - * probing. + * Process the memory map. This gives us the PAL locations, + * the I/O port base address, the available memory regions + * for initializing the physical memory map. */ for (md = efi_md_first(); md != NULL; md = efi_md_next(md)) { + mdlen = md->md_pages * EFI_PAGE_SIZE; switch (md->md_type) { case EFI_MD_TYPE_IOPORT: ia64_port_base = (uintptr_t)pmap_mapdev(md->md_phys, - md->md_pages * EFI_PAGE_SIZE); + mdlen); break; case EFI_MD_TYPE_PALCODE: - ia64_pal_size = md->md_pages * EFI_PAGE_SIZE; ia64_pal_base = md->md_phys; + ia64_pal_size = mdlen; + /*FALLTHROUGH*/ + case EFI_MD_TYPE_BAD: + case EFI_MD_TYPE_FIRMWARE: + case EFI_MD_TYPE_RECLAIM: + case EFI_MD_TYPE_RT_CODE: + case EFI_MD_TYPE_RT_DATA: + /* Don't use these memory regions. */ + ia64_physmem_track(md->md_phys, mdlen); + break; + case EFI_MD_TYPE_BS_CODE: + case EFI_MD_TYPE_BS_DATA: + case EFI_MD_TYPE_CODE: + case EFI_MD_TYPE_DATA: + case EFI_MD_TYPE_FREE: + /* These are ok to use. */ + ia64_physmem_add(md->md_phys, mdlen); break; } } + /* + * Remove the PBVM and its page table from phys_avail. The loader + * passes the physical address of the page table to us. The virtual + * address of the page table is fixed. + * Track and the PBVM limit for later use. + */ + ia64_physmem_delete(bootinfo->bi_pbvm_pgtbl, bootinfo->bi_pbvm_pgtblsz); + pbvm_pgtbl_ent = (void *)IA64_PBVM_PGTBL; + pbvm_pgtbl_lim = (void *)(IA64_PBVM_PGTBL + bootinfo->bi_pbvm_pgtblsz); + while (pbvm_pgtbl_ent < pbvm_pgtbl_lim) { + if ((*pbvm_pgtbl_ent & PTE_PRESENT) == 0) + break; + ia64_physmem_delete(*pbvm_pgtbl_ent & PTE_PPN_MASK, + IA64_PBVM_PAGE_SIZE); + pbvm_pgtbl_ent++; + } + + /* Finalize physical memory datastructures */ + ia64_physmem_fini(); + metadata_missing = 0; if (bootinfo->bi_modulep) preload_metadata = (caddr_t)bootinfo->bi_modulep; @@ -757,9 +773,8 @@ ia64_init(void) bootverbose = 1; /* - * Find the beginning and end of the kernel. + * Find the end of the kernel. */ - kernstart = trunc_page(kernel_text); #ifdef DDB ksym_start = bootinfo->bi_symtab; ksym_end = bootinfo->bi_esymtab; @@ -772,16 +787,6 @@ ia64_init(void) kernend = round_page(bootinfo->bi_kernend); /* - * Region 6 is direct mapped UC and region 7 is direct mapped - * WC. The details of this is controlled by the Alt {I,D}TLB - * handlers. Here we just make sure that they have the largest - * possible page size to minimise TLB usage. - */ - ia64_set_rr(IA64_RR_BASE(6), (6 << 8) | (PAGE_SHIFT << 2)); - ia64_set_rr(IA64_RR_BASE(7), (7 << 8) | (PAGE_SHIFT << 2)); - ia64_srlz_d(); - - /* * Wire things up so we can call the firmware. */ map_pal_code(); @@ -828,92 +833,6 @@ ia64_init(void) freeenv(p); } - kernstartpfn = atop(IA64_RR_MASK(kernstart)); - kernendpfn = atop(IA64_RR_MASK(kernend)); - - /* - * Size the memory regions and load phys_avail[] with the results. - */ - - /* - * Find out how much memory is available, by looking at - * the memory descriptors. - */ - -#ifdef DEBUG_MD - printf("Memory descriptor count: %d\n", mdcount); -#endif - - phys_avail_cnt = 0; - for (md = efi_md_first(); md != NULL; md = efi_md_next(md)) { -#ifdef DEBUG_MD - printf("MD %p: type %d pa 0x%lx cnt 0x%lx\n", md, - md->md_type, md->md_phys, md->md_pages); -#endif - - pfn0 = ia64_btop(round_page(md->md_phys)); - pfn1 = ia64_btop(trunc_page(md->md_phys + md->md_pages * 4096)); - if (pfn1 <= pfn0) - continue; - - if (md->md_type != EFI_MD_TYPE_FREE) - continue; - - /* - * We have a memory descriptor that describes conventional - * memory that is for general use. We must determine if the - * loader has put the kernel in this region. - */ - physmem += (pfn1 - pfn0); - if (pfn0 <= kernendpfn && kernstartpfn <= pfn1) { - /* - * Must compute the location of the kernel - * within the segment. - */ -#ifdef DEBUG_MD - printf("Descriptor %p contains kernel\n", mp); -#endif - if (pfn0 < kernstartpfn) { - /* - * There is a chunk before the kernel. - */ -#ifdef DEBUG_MD - printf("Loading chunk before kernel: " - "0x%lx / 0x%lx\n", pfn0, kernstartpfn); -#endif - phys_avail[phys_avail_cnt] = ia64_ptob(pfn0); - phys_avail[phys_avail_cnt+1] = ia64_ptob(kernstartpfn); - phys_avail_cnt += 2; - } - if (kernendpfn < pfn1) { - /* - * There is a chunk after the kernel. - */ -#ifdef DEBUG_MD - printf("Loading chunk after kernel: " - "0x%lx / 0x%lx\n", kernendpfn, pfn1); -#endif - phys_avail[phys_avail_cnt] = ia64_ptob(kernendpfn); - phys_avail[phys_avail_cnt+1] = ia64_ptob(pfn1); - phys_avail_cnt += 2; - } - } else { - /* - * Just load this cluster as one chunk. - */ -#ifdef DEBUG_MD - printf("Loading descriptor %d: 0x%lx / 0x%lx\n", i, - pfn0, pfn1); -#endif - phys_avail[phys_avail_cnt] = ia64_ptob(pfn0); - phys_avail[phys_avail_cnt+1] = ia64_ptob(pfn1); - phys_avail_cnt += 2; - - } - } - phys_avail[phys_avail_cnt] = 0; - - Maxmem = physmem; init_param2(physmem); /* diff --git a/sys/ia64/ia64/physmem.c b/sys/ia64/ia64/physmem.c new file mode 100644 index 0000000..9299467 --- /dev/null +++ b/sys/ia64/ia64/physmem.c @@ -0,0 +1,195 @@ +/*- + * Copyright (c) 2012 Marcel Moolenaar + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/systm.h> + +#include <machine/md_var.h> +#include <machine/vmparam.h> + +static u_int phys_avail_segs; + +vm_paddr_t phys_avail[2 * VM_PHYSSEG_MAX + 2]; + +vm_paddr_t paddr_max; + +long realmem; + +static u_int +ia64_physmem_find(vm_paddr_t base, vm_paddr_t lim) +{ + u_int idx; + + for (idx = 0; phys_avail[idx + 1] != 0; idx += 2) { + if (phys_avail[idx] >= lim || + phys_avail[idx + 1] > base) + break; + } + return (idx); +} + +static int +ia64_physmem_insert(u_int idx, vm_paddr_t base, vm_paddr_t lim) +{ + u_int ridx; + + if (phys_avail_segs == VM_PHYSSEG_MAX) + return (ENOMEM); + + ridx = phys_avail_segs * 2; + while (idx < ridx) { + phys_avail[ridx + 1] = phys_avail[ridx - 1]; + phys_avail[ridx] = phys_avail[ridx - 2]; + ridx -= 2; + } + phys_avail[idx] = base; + phys_avail[idx + 1] = lim; + phys_avail_segs++; + return (0); +} + +static int +ia64_physmem_remove(u_int idx) +{ + + if (phys_avail_segs == 0) + return (ENOENT); + do { + phys_avail[idx] = phys_avail[idx + 2]; + phys_avail[idx + 1] = phys_avail[idx + 3]; + idx += 2; + } while (phys_avail[idx + 1] != 0); + phys_avail_segs--; + return (0); +} + +int +ia64_physmem_add(vm_paddr_t base, vm_size_t len) +{ + vm_paddr_t lim; + u_int idx; + + realmem += len; + + lim = base + len; + idx = ia64_physmem_find(base, lim); + if (phys_avail[idx] == lim) { + phys_avail[idx] = base; + return (0); + } + if (idx > 0 && phys_avail[idx - 1] == base) { + phys_avail[idx - 1] = lim; + return (0); + } + return (ia64_physmem_insert(idx, base, lim)); +} + +int +ia64_physmem_delete(vm_paddr_t base, vm_size_t len) +{ + vm_paddr_t lim; + u_int idx; + + lim = base + len; + idx = ia64_physmem_find(base, lim); + if (phys_avail[idx] >= lim || phys_avail[idx + 1] == 0) + return (ENOENT); + if (phys_avail[idx] < base && phys_avail[idx + 1] > lim) { + len = phys_avail[idx + 1] - lim; + phys_avail[idx + 1] = base; + base = lim; + lim = base + len; + return (ia64_physmem_insert(idx + 2, base, lim)); + } else { + if (phys_avail[idx] == base) + phys_avail[idx] = lim; + if (phys_avail[idx + 1] == lim) + phys_avail[idx + 1] = base; + if (phys_avail[idx] >= phys_avail[idx + 1]) + return (ia64_physmem_remove(idx)); + } + return (0); +} + +int +ia64_physmem_fini(void) +{ + vm_paddr_t base, lim, size; + u_int idx; + + idx = 0; + while (phys_avail[idx + 1] != 0) { + base = round_page(phys_avail[idx]); + lim = trunc_page(phys_avail[idx + 1]); + if (base < lim) { + phys_avail[idx] = base; + phys_avail[idx + 1] = lim; + size = lim - base; + physmem += atop(size); + paddr_max = lim; + idx += 2; + } else + ia64_physmem_remove(idx); + } + + /* + * Round realmem to a multple of 128MB. Hopefully that compensates + * for any loss of DRAM that isn't accounted for in the memory map. + * I'm thinking legacy BIOS or VGA here. In any case, it's ok if + * we got it wrong, because we don't actually use realmem. It's + * just for show... + */ + size = 1U << 27; + realmem = (realmem + size - 1) & ~(size - 1); + realmem = atop(realmem); + return (0); +} + +int +ia64_physmem_init(void) +{ + + /* Nothing to do just yet. */ + return (0); +} + +int +ia64_physmem_track(vm_paddr_t base, vm_size_t len) +{ + + realmem += len; + return (0); +} + +vm_paddr_t +ia64_physmem_alloc(vm_size_t len, vm_size_t align) +{ + + return (0); +} diff --git a/sys/ia64/include/md_var.h b/sys/ia64/include/md_var.h index 8286936..09c55a8 100644 --- a/sys/ia64/include/md_var.h +++ b/sys/ia64/include/md_var.h @@ -73,8 +73,7 @@ struct ia64_init_return { }; extern uint64_t ia64_lapic_addr; - -extern long Maxmem; +extern vm_paddr_t paddr_max; extern u_int busdma_swi_pending; void *acpi_find_table(const char *sig); @@ -93,6 +92,12 @@ int ia64_highfp_save(struct thread *); int ia64_highfp_save_ipi(void); struct ia64_init_return ia64_init(void); u_int ia64_itc_freq(void); +int ia64_physmem_add(vm_paddr_t, vm_size_t); +vm_paddr_t ia64_physmem_alloc(vm_size_t, vm_size_t); +int ia64_physmem_delete(vm_paddr_t, vm_size_t); +int ia64_physmem_fini(void); +int ia64_physmem_init(void); +int ia64_physmem_track(vm_paddr_t, vm_size_t); void ia64_probe_sapics(void); void ia64_sync_icache(vm_offset_t, vm_size_t); void interrupt(struct trapframe *); diff --git a/sys/ia64/include/param.h b/sys/ia64/include/param.h index d996441..aa837d8 100644 --- a/sys/ia64/include/param.h +++ b/sys/ia64/include/param.h @@ -110,9 +110,6 @@ #define atop(x) ((unsigned long)(x) >> PAGE_SHIFT) #define ptoa(x) ((unsigned long)(x) << PAGE_SHIFT) -#define ia64_btop(x) ((unsigned long)(x) >> PAGE_SHIFT) -#define ia64_ptob(x) ((unsigned long)(x) << PAGE_SHIFT) - #define pgtok(x) ((x) * (PAGE_SIZE / 1024)) #endif /* !_IA64_INCLUDE_PARAM_H_ */ |