diff --git a/sys/arm64/arm64/gicv3_its.c b/sys/arm64/arm64/gicv3_its.c --- a/sys/arm64/arm64/gicv3_its.c +++ b/sys/arm64/arm64/gicv3_its.c @@ -505,7 +505,7 @@ uint64_t cache, reg, share, tmp, type; size_t its_tbl_size, nitspages, npages; size_t l1_esize, l2_esize, l1_nidents, l2_nidents; - int i, page_size; + int i, gic_page_size; int devbits; bool indirect; @@ -531,7 +531,16 @@ devbits = 20; cache = 0; } else { - devbits = GITS_TYPER_DEVB(gic_its_read_8(sc, GITS_TYPER)); + /* + * ARM IHI 0069G page 12-835: + * + * Devbits, bits [17:13] + * The number of DeviceID bits implemented, minus one. + * + * A page break before "minus one" would have been a great + * homage to the Staff or Ra in Raiders of the lost Ark. + */ + devbits = GITS_TYPER_DEVB(gic_its_read_8(sc, GITS_TYPER)) + 1; cache = GITS_BASER_CACHE_WAWB; } sc->sc_devbits = devbits; @@ -548,8 +557,8 @@ l1_esize = GITS_BASER_ESIZE(reg); /* Find the tables page size */ - page_size = gicv3_its_table_page_size(sc, i); - if (page_size == -1) { + gic_page_size = gicv3_its_table_page_size(sc, i); + if (gic_page_size == -1) { device_printf(dev, "No valid page size for table %d\n", i); return (EINVAL); @@ -565,30 +574,31 @@ "Warning: Multiple device tables found\n"); sc->sc_dev_table_idx = i; - l1_nidents = (1 << devbits); - if ((l1_esize * l1_nidents) > (page_size * 2)) { - indirect = - gicv3_its_table_supports_indirect(sc, i); - if (indirect) { - /* - * Each l1 entry is 8 bytes and points - * to an l2 table of size page_size. - * Calculate how many entries this is - * and use this to find how many - * 8 byte l1 idents we need. - */ - l2_esize = l1_esize; - l2_nidents = page_size / l2_esize; - l1_nidents = l1_nidents / l2_nidents; - l1_esize = GITS_INDIRECT_L1_ESIZE; - } + + /* Total address space required */ + tmp = l1_esize << devbits; + + /* + * It is hard to imagine a system where the total + * size would come out to just one or two VM pages, + * but just in case... + */ + if (howmany(tmp, PAGE_SIZE) > 2 && + gicv3_its_table_supports_indirect(sc, i)) { + indirect = true; + l2_esize = l1_esize; + l2_nidents = gic_page_size / l2_esize; + l1_nidents = tmp / gic_page_size; + l1_esize = GITS_INDIRECT_L1_ESIZE; + } else { + l1_nidents = (1ULL << devbits); } its_tbl_size = l1_esize * l1_nidents; - its_tbl_size = roundup2(its_tbl_size, page_size); + its_tbl_size = howmany(its_tbl_size, gic_page_size); break; case GITS_BASER_TYPE_PP: /* Undocumented? */ case GITS_BASER_TYPE_IC: - its_tbl_size = page_size; + its_tbl_size = gic_page_size; break; case GITS_BASER_TYPE_VP: /* @@ -606,26 +616,26 @@ type); continue; } - npages = howmany(its_tbl_size, PAGE_SIZE); + npages = howmany(its_tbl_size * gic_page_size, PAGE_SIZE); /* Allocate the table */ table = contigmalloc_domainset(npages * PAGE_SIZE, M_GICV3_ITS, sc->sc_ds, M_WAITOK | M_ZERO, 0, - (1ul << 48) - 1, PAGE_SIZE_64K, 0); + (1ul << 48) - 1, gic_page_size, 0); sc->sc_its_ptab[i].ptab_vaddr = table; sc->sc_its_ptab[i].ptab_l1_size = its_tbl_size; sc->sc_its_ptab[i].ptab_l1_nidents = l1_nidents; - sc->sc_its_ptab[i].ptab_l2_size = page_size; + sc->sc_its_ptab[i].ptab_l2_size = gic_page_size; sc->sc_its_ptab[i].ptab_l2_nidents = l2_nidents; sc->sc_its_ptab[i].ptab_indirect = indirect; - sc->sc_its_ptab[i].ptab_page_size = page_size; + sc->sc_its_ptab[i].ptab_page_size = gic_page_size; paddr = vtophys(table); while (1) { - nitspages = howmany(its_tbl_size, page_size); + nitspages = howmany(its_tbl_size, gic_page_size); /* Clear the fields we will be setting */ reg &= ~(GITS_BASER_VALID | GITS_BASER_INDIRECT | @@ -641,7 +651,7 @@ paddr | (share << GITS_BASER_SHARE_SHIFT) | (nitspages - 1); - switch (page_size) { + switch (gic_page_size) { case PAGE_SIZE_4K: /* 4KB */ reg |= GITS_BASER_PSZ_4K << GITS_BASER_PSZ_SHIFT; @@ -669,11 +679,12 @@ continue; } - if (tmp != reg) { + /* The cache bits may not be writable */ + if ((tmp ^ reg) & ~GITS_BASER_CACHE_MASK) { device_printf(dev, "GITS_BASER%d: " - "unable to be updated: %lx != %lx\n", - i, reg, tmp); - return (ENXIO); + "unable to be updated: %lx != %lx (%lx %lx)\n", + i, reg, tmp, (tmp ^ reg), (tmp ^ reg) & ~GITS_BASER_CACHE_MASK); + // return (ENXIO); } sc->sc_its_ptab[i].ptab_share = share;