diff options
author | marcel <marcel@FreeBSD.org> | 2015-08-12 15:26:32 +0000 |
---|---|---|
committer | marcel <marcel@FreeBSD.org> | 2015-08-12 15:26:32 +0000 |
commit | bacabe8a7e513430a2b52fb22a5867858c7627ca (patch) | |
tree | 653270464aff78cefbfe92cd13e73b4e89b88d54 /sys/amd64/amd64/pmap.c | |
parent | a00534bfebff3ae3ad1b4bdfce62355d4347541d (diff) | |
download | FreeBSD-src-bacabe8a7e513430a2b52fb22a5867858c7627ca.zip FreeBSD-src-bacabe8a7e513430a2b52fb22a5867858c7627ca.tar.gz |
Better support memory mapped console devices, such as VGA and EFI
frame buffers and memory mapped UARTs.
1. Delay calling cninit() until after pmap_bootstrap(). This makes
sure we have PMAP initialized enough to add translations. Keep
kdb_init() after cninit() so that we have console when we need
to break into the debugger on boot.
2. Unfortunately, the ATPIC code had be moved as well so as to
avoid a spurious trap #30. The reason for which is not known
at this time.
3. In pmap_mapdev_attr(), when we need to map a device prior to the
VM system being initialized, use virtual_avail as the KVA to map
the device at. In particular, avoid using the direct map on amd64
because we can't demote by virtue of not being able to allocate
yet. Keep track of the translation.
Re-use the translation after the VM has been initialized to not
waste KVA and to satisfy the assumption in uart(4) that the handle
returned for the low-level console is the same as later returned
when the device is probed and attached.
4. In pmap_unmapdev() remove the mapping from the table when called
pre-init. Otherwise keep the mapping. During bus probe and attach
device resources are mapped and unmapped multiple times, which
would have us destroy the mapping used by the low-level console.
5. In pmap_init(), set pmap_initialized to signal that we're not
pre-init anymore. On amd64, bring the direct map in sync with the
translations created at that time.
6. Implement bus_space_map() and bus_space_unmap() for real: when
the tag corresponds to memory space, call the corresponding
pmap_mapdev() and pmap_unmapdev() functions to construct and
actual handle.
7. In efifb.c and vt_vga.c, remove the crutches and hacks and simply
call pmap_mapdev_attr() or bus_space_map() as desired.
Notes:
1. uart(4) already used bus_space_map() during low-level console
setup but since serial ports have traditionally been I/O port
based, the lack of a proper implementation for said function
was not a problem. It has always supported memory mapped UARTs
for low-level consoles by setting hw.uart.console accordingly.
2. The use of the direct map on amd64 without setting caching
attributes has been a bigger problem than previously thought.
This change has the fortunate (and unexpected) side-effect of
fixing various EFI frame buffer problems (though not all).
PR: 191564, 194952
Special thanks to:
1. XipLink, Inc -- generously donated an Intel Bay Trail E3800
based eval board (ADLE3800PC).
2. The FreeBSD Foundation, in particular emaste@ -- for UEFI
support in general and testing.
3. Everyone who tested the proposed for PR 191564.
4. jhb@ and kib@ for being a soundboard and applying a clue bat
if so needed.
Diffstat (limited to 'sys/amd64/amd64/pmap.c')
-rw-r--r-- | sys/amd64/amd64/pmap.c | 106 |
1 files changed, 91 insertions, 15 deletions
diff --git a/sys/amd64/amd64/pmap.c b/sys/amd64/amd64/pmap.c index 780d0e3..1e64fc8 100644 --- a/sys/amd64/amd64/pmap.c +++ b/sys/amd64/amd64/pmap.c @@ -363,6 +363,18 @@ static u_int64_t DMPDphys; /* phys addr of direct mapped level 2 */ static u_int64_t DMPDPphys; /* phys addr of direct mapped level 3 */ static int ndmpdpphys; /* number of DMPDPphys pages */ +/* + * pmap_mapdev support pre initialization (i.e. console) + */ +#define PMAP_PREINIT_MAPPING_COUNT 8 +static struct pmap_preinit_mapping { + vm_paddr_t pa; + vm_offset_t va; + vm_size_t sz; + int mode; +} pmap_preinit_mapping[PMAP_PREINIT_MAPPING_COUNT]; +static int pmap_initialized; + static struct rwlock_padalign pvh_global_lock; /* @@ -1016,6 +1028,7 @@ pmap_page_init(vm_page_t m) void pmap_init(void) { + struct pmap_preinit_mapping *ppim; vm_page_t mpte; vm_size_t s; int i, pv_npg; @@ -1083,6 +1096,22 @@ pmap_init(void) M_WAITOK | M_ZERO); for (i = 0; i < pv_npg; i++) TAILQ_INIT(&pv_table[i].pv_list); + + pmap_initialized = 1; + for (i = 0; i < PMAP_PREINIT_MAPPING_COUNT; i++) { + ppim = pmap_preinit_mapping + i; + if (ppim->va == 0) + continue; + /* Make the direct map consistent */ + if (ppim->pa < dmaplimit && ppim->pa + ppim->sz < dmaplimit) { + (void)pmap_change_attr(PHYS_TO_DMAP(ppim->pa), + ppim->sz, ppim->mode); + } + if (!bootverbose) + continue; + printf("PPIM %u: PA=%#lx, VA=%#lx, size=%#lx, mode=%#x\n", i, + ppim->pa, ppim->va, ppim->sz, ppim->mode); + } } static SYSCTL_NODE(_vm_pmap, OID_AUTO, pde, CTLFLAG_RD, 0, @@ -6105,24 +6134,54 @@ pmap_pde_attr(pd_entry_t *pde, int cache_bits, int mask) void * pmap_mapdev_attr(vm_paddr_t pa, vm_size_t size, int mode) { + struct pmap_preinit_mapping *ppim; vm_offset_t va, offset; vm_size_t tmpsize; + int i; - /* - * If the specified range of physical addresses fits within the direct - * map window, use the direct map. - */ - if (pa < dmaplimit && pa + size < dmaplimit) { - va = PHYS_TO_DMAP(pa); - if (!pmap_change_attr(va, size, mode)) - return ((void *)va); - } offset = pa & PAGE_MASK; size = round_page(offset + size); - va = kva_alloc(size); - if (!va) - panic("pmap_mapdev: Couldn't alloc kernel virtual memory"); pa = trunc_page(pa); + + if (!pmap_initialized) { + va = 0; + for (i = 0; i < PMAP_PREINIT_MAPPING_COUNT; i++) { + ppim = pmap_preinit_mapping + i; + if (ppim->va == 0) { + ppim->pa = pa; + ppim->sz = size; + ppim->mode = mode; + ppim->va = virtual_avail; + virtual_avail += size; + va = ppim->va; + break; + } + } + if (va == 0) + panic("%s: too many preinit mappings", __func__); + } else { + /* + * If we have a preinit mapping, re-use it. + */ + for (i = 0; i < PMAP_PREINIT_MAPPING_COUNT; i++) { + ppim = pmap_preinit_mapping + i; + if (ppim->pa == pa && ppim->sz == size && + ppim->mode == mode) + return ((void *)(ppim->va + offset)); + } + /* + * If the specified range of physical addresses fits within + * the direct map window, use the direct map. + */ + if (pa < dmaplimit && pa + size < dmaplimit) { + va = PHYS_TO_DMAP(pa); + if (!pmap_change_attr(va, size, mode)) + return ((void *)(va + offset)); + } + va = kva_alloc(size); + if (va == 0) + panic("%s: Couldn't allocate KVA", __func__); + } for (tmpsize = 0; tmpsize < size; tmpsize += PAGE_SIZE) pmap_kenter_attr(va + tmpsize, pa + tmpsize, mode); pmap_invalidate_range(kernel_pmap, va, va + tmpsize); @@ -6147,15 +6206,32 @@ pmap_mapbios(vm_paddr_t pa, vm_size_t size) void pmap_unmapdev(vm_offset_t va, vm_size_t size) { - vm_offset_t base, offset; + struct pmap_preinit_mapping *ppim; + vm_offset_t offset; + int i; /* If we gave a direct map region in pmap_mapdev, do nothing */ if (va >= DMAP_MIN_ADDRESS && va < DMAP_MAX_ADDRESS) return; - base = trunc_page(va); offset = va & PAGE_MASK; size = round_page(offset + size); - kva_free(base, size); + va = trunc_page(va); + for (i = 0; i < PMAP_PREINIT_MAPPING_COUNT; i++) { + ppim = pmap_preinit_mapping + i; + if (ppim->va == va && ppim->sz == size) { + if (pmap_initialized) + return; + ppim->pa = 0; + ppim->va = 0; + ppim->sz = 0; + ppim->mode = 0; + if (va + size == virtual_avail) + virtual_avail = va; + return; + } + } + if (pmap_initialized) + kva_free(va, size); } /* |