Fix system hang when large FDT is in use

Summary:
Kernel maps only one page of FDT. When FDT is more than one page in size, data
TLB miss occurs on memmove() when FDT is moved to kernel storage
(sys/powerpc/booke/booke_machdep.c, booke_init())

This introduces a pmap_early_io_unmap() to complement pmap_early_io_map(), which
can be used for any early I/O mapping, but currently is only used when mapping
the fdt.

Submitted by:	Ivan Krivonos <int0dster_gmail.com>
Differential Revision: https://reviews.freebsd.org/D7605
This commit is contained in:
Justin Hibbits 2016-08-24 03:51:40 +00:00
parent e626c40eb5
commit 60152a4037
3 changed files with 38 additions and 0 deletions

View File

@ -249,6 +249,7 @@ static int
booke_check_for_fdt(uint32_t arg1, vm_offset_t *dtbp)
{
void *ptr;
int fdt_size;
if (arg1 % 8 != 0)
return (-1);
@ -257,6 +258,19 @@ booke_check_for_fdt(uint32_t arg1, vm_offset_t *dtbp)
if (fdt_check_header(ptr) != 0)
return (-1);
/*
* Read FDT total size from the header of FDT.
* This for sure hits within first page which is
* already mapped.
*/
fdt_size = fdt_totalsize((void *)ptr);
/*
* Ok, arg1 points to FDT, so we need to map it in.
* First, unmap this page and then map FDT again with full size
*/
pmap_early_io_unmap((vm_offset_t)ptr, PAGE_SIZE);
ptr = (void *)pmap_early_io_map(arg1, fdt_size);
*dtbp = (vm_offset_t)ptr;
return (0);

View File

@ -3419,6 +3419,29 @@ tlb1_init()
set_mas4_defaults();
}
void
pmap_early_io_unmap(vm_offset_t va, vm_size_t size)
{
int i;
tlb_entry_t e;
for (i = 0; i < TLB1_ENTRIES && size > 0; i ++) {
tlb1_read_entry(&e, i);
if (!(e.mas1 & MAS1_VALID))
continue;
/*
* FIXME: this code does not work if VA region
* spans multiple TLB entries. This does not cause
* problems right now but shall be fixed in the future
*/
if (va >= e.virt && (va + size) <= (e.virt + e.size)) {
size -= e.size;
e.mas1 &= ~MAS1_VALID;
tlb1_write_entry(&e, i);
}
}
}
vm_offset_t
pmap_early_io_map(vm_paddr_t pa, vm_size_t size)
{

View File

@ -260,6 +260,7 @@ extern vm_offset_t msgbuf_phys;
extern int pmap_bootstrapped;
vm_offset_t pmap_early_io_map(vm_paddr_t pa, vm_size_t size);
void pmap_early_io_unmap(vm_offset_t va, vm_size_t size);
#endif