diff options
Diffstat (limited to 'usr/src/uts/sun4u/starfire/os/pda.c')
| -rw-r--r-- | usr/src/uts/sun4u/starfire/os/pda.c | 622 |
1 files changed, 622 insertions, 0 deletions
diff --git a/usr/src/uts/sun4u/starfire/os/pda.c b/usr/src/uts/sun4u/starfire/os/pda.c new file mode 100644 index 0000000000..b977e203b0 --- /dev/null +++ b/usr/src/uts/sun4u/starfire/os/pda.c @@ -0,0 +1,622 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Starfire Post Descriptor Array (post2obp) management. + */ + +#include <sys/debug.h> +#include <sys/types.h> +#include <sys/errno.h> +#include <sys/cpuvar.h> +#include <sys/dditypes.h> +#include <sys/conf.h> +#include <sys/ddi.h> +#include <sys/kmem.h> +#include <sys/sunddi.h> +#include <sys/sunndi.h> +#include <sys/vm.h> +#include <vm/seg.h> +#include <vm/seg_kmem.h> +#include <vm/seg_kp.h> +#include <sys/machsystm.h> +#include <sys/starfire.h> + +#include <sys/cpu_sgnblk_defs.h> +#include <sys/pda.h> +#include <sys/cpu_sgn.h> + +extern struct cpu *SIGBCPU; +extern cpu_sgnblk_t *cpu_sgnblkp[]; + +extern uint64_t mc_get_mem_alignment(); +extern uint64_t mc_asr_to_pa(uint_t mcreg); + +static post2obp_info_t *cpu_p2o_mapin(int cpuid); +static void cpu_p2o_mapout(int cpuid, post2obp_info_t *p2o); +static void p2o_update_checksum(post2obp_info_t *p2o); +static uint_t p2o_calc_checksum(post2obp_info_t *p2o); +static void p2o_mem_sort(post2obp_info_t *p2o); +static void p2o_mem_coalesce(post2obp_info_t *p2o); + +typedef struct { + post2obp_info_t *p2o_ptr; + int p2o_cpuid; +} p2o_info_t; + +/* + * PDA management routines. Should ultimately be made + * accessible to other Starfire subsystems, but for + * now we'll leave it here. + */ +pda_handle_t +pda_open() +{ + p2o_info_t *pip; + + if (SIGBCPU == NULL) { + cmn_err(CE_WARN, "pda_open: SIGBCPU is NULL"); + return (NULL); + } + + pip = (p2o_info_t *)kmem_alloc(sizeof (p2o_info_t), KM_SLEEP); + + pip->p2o_cpuid = (int)SIGBCPU->cpu_id; + pip->p2o_ptr = cpu_p2o_mapin(pip->p2o_cpuid); + + if (pip->p2o_ptr == NULL) { + kmem_free((caddr_t)pip, sizeof (p2o_info_t)); + return ((pda_handle_t)NULL); + } else { + return ((pda_handle_t)pip); + } +} + +void +pda_close(pda_handle_t ph) +{ + p2o_info_t *pip; + + if ((pip = (p2o_info_t *)ph) == NULL) + return; + + cpu_p2o_mapout(pip->p2o_cpuid, pip->p2o_ptr); + + kmem_free((caddr_t)pip, sizeof (p2o_info_t)); +} + +int +pda_board_present(pda_handle_t ph, int boardnum) +{ + ushort_t bda_board; + post2obp_info_t *p2o = ((p2o_info_t *)ph)->p2o_ptr; + + bda_board = p2o->p2o_bdinfo[boardnum].bda_board; + + if ((bda_board & BDAN_MASK) != BDAN_GOOD) + return (0); + else + return (1); +} + +void * +pda_get_board_info(pda_handle_t ph, int boardnum) +{ + post2obp_info_t *p2o = ((p2o_info_t *)ph)->p2o_ptr; + + return ((void *)&(p2o->p2o_bdinfo[boardnum])); +} + +uint_t +pda_get_mem_size(pda_handle_t ph, int boardnum) +{ + int c; + pgcnt_t npages; + uint_t asr; + pfn_t basepfn, endpfn; + uint64_t basepa, endpa; + post2obp_info_t *p2o = ((p2o_info_t *)ph)->p2o_ptr; + + if (boardnum == -1) + return (p2o->p2o_memtotal.Memt_NumPages); + + asr = p2o->p2o_bdminfo[boardnum].bmda_adr; + + basepa = mc_asr_to_pa(asr); + /* + * Put on MC alignment. + */ + endpa = mc_get_mem_alignment(); + basepa &= ~(endpa - 1); + endpa += basepa; + basepfn = (pfn_t)(basepa >> PAGESHIFT); + endpfn = (pfn_t)(endpa >> PAGESHIFT); + + npages = 0; + + for (c = 0; c < p2o->p2o_memtotal.Memt_NumChunks; c++) { + pfn_t c_basepfn, c_endpfn; + + c_basepfn = (pfn_t)p2o->p2o_mchunks[c].Memc_StartAddress + >> (PAGESHIFT - BDA_PAGESHIFT); + c_endpfn = (pfn_t)p2o->p2o_mchunks[c].Memc_Size + >> (PAGESHIFT - BDA_PAGESHIFT); + c_endpfn += c_basepfn; + + if ((endpfn <= c_basepfn) || (basepfn >= c_endpfn)) + continue; + + c_basepfn = MAX(c_basepfn, basepfn); + c_endpfn = MIN(c_endpfn, endpfn); + ASSERT(c_basepfn <= c_endpfn); + + npages += c_endpfn - c_basepfn; + } + + return (npages); +} + +void +pda_mem_add_span(pda_handle_t ph, uint64_t basepa, uint64_t nbytes) +{ + post2obp_info_t *p2o = ((p2o_info_t *)ph)->p2o_ptr; + int c, nchunks; + pfn_t a_pfn, a_npgs; + + ASSERT(p2o); + + nchunks = p2o->p2o_memtotal.Memt_NumChunks; + a_pfn = (pfn_t)(basepa >> BDA_PAGESHIFT); + a_npgs = (pfn_t)(nbytes >> BDA_PAGESHIFT); + + for (c = 0; c < nchunks; c++) { + int cend; + + if (a_pfn <= p2o->p2o_mchunks[c].Memc_StartAddress) { + for (cend = nchunks; cend > c; cend--) + p2o->p2o_mchunks[cend] = + p2o->p2o_mchunks[cend - 1]; + break; + } + } + p2o->p2o_mchunks[c].Memc_StartAddress = a_pfn; + p2o->p2o_mchunks[c].Memc_Size = a_npgs; + nchunks++; + + p2o->p2o_memtotal.Memt_NumChunks = nchunks; + p2o->p2o_memtotal.Memt_NumPages += a_npgs; + + p2o_mem_sort(p2o); + p2o_mem_coalesce(p2o); + p2o_update_checksum(p2o); +} + +void +pda_mem_del_span(pda_handle_t ph, uint64_t basepa, uint64_t nbytes) +{ + post2obp_info_t *p2o = ((p2o_info_t *)ph)->p2o_ptr; + int c, o_nchunks, n_nchunks; + pfn_t d_pfn; + pgcnt_t d_npgs, npages; + MemChunk_t *mp, *endp; + + ASSERT(p2o); + + d_pfn = (pfn_t)(basepa >> BDA_PAGESHIFT); + d_npgs = (pgcnt_t)(nbytes >> BDA_PAGESHIFT); + n_nchunks = o_nchunks = p2o->p2o_memtotal.Memt_NumChunks; + endp = &(p2o->p2o_mchunks[o_nchunks]); + npages = 0; + + for (c = 0; c < o_nchunks; c++) { + uint_t p_pfn, p_npgs; + + p_pfn = p2o->p2o_mchunks[c].Memc_StartAddress; + p_npgs = p2o->p2o_mchunks[c].Memc_Size; + if (p_npgs == 0) + continue; + + if (((d_pfn + d_npgs) <= p_pfn) || + (d_pfn >= (p_pfn + p_npgs))) { + npages += p_npgs; + continue; + } + + if (d_pfn < p_pfn) { + if ((d_pfn + d_npgs) >= (p_pfn + p_npgs)) { + /* + * Entire chunk goes away. + */ + p_pfn = p_npgs = 0; + } else { + p_npgs -= d_pfn + d_npgs - p_pfn; + p_pfn = d_pfn + d_npgs; + } + } else if (d_pfn == p_pfn) { + if ((d_pfn + d_npgs) >= (p_pfn + p_npgs)) { + p_pfn = p_npgs = 0; + } else { + p_npgs -= d_npgs; + p_pfn += d_npgs; + } + } else { + if ((d_pfn + d_npgs) >= (p_pfn + p_npgs)) { + p_npgs = d_pfn - p_pfn; + npages += p_npgs; + } else { + /* + * Ugh, got to split a + * memchunk, we're going to + * need an extra one. It's + * gotten from the end. + */ + endp->Memc_StartAddress = d_pfn + d_npgs; + endp->Memc_Size = (p_pfn + p_npgs) + - (d_pfn + d_npgs); + npages += endp->Memc_Size; + endp++; + n_nchunks++; + p_npgs = d_pfn - p_pfn; + } + } + + p2o->p2o_mchunks[c].Memc_StartAddress = p_pfn; + p2o->p2o_mchunks[c].Memc_Size = p_npgs; + if (p_npgs == 0) + n_nchunks--; + npages += p_npgs; + } + p2o->p2o_memtotal.Memt_NumChunks = n_nchunks; + p2o->p2o_memtotal.Memt_NumPages = npages; + + /* + * There is a possibility we created holes in the memchunk list + * due to memchunks that went away. Before we can sort and + * coalesce we need to "pull up" the end of the memchunk list + * and get rid of any holes. + * endp = points to the last empty memchunk entry. + */ + for (mp = &(p2o->p2o_mchunks[0]); mp < endp; mp++) { + register MemChunk_t *mmp; + + if (mp->Memc_Size) + continue; + + for (mmp = mp; mmp < endp; mmp++) + *mmp = *(mmp + 1); + mp--; + endp--; + } + ASSERT(endp == &(p2o->p2o_mchunks[n_nchunks])); + + p2o_mem_sort(p2o); + p2o_mem_coalesce(p2o); + p2o_update_checksum(p2o); +} + +/* + * Synchonize all memory attributes (currently just MC ADRs [aka ASR]) + * with PDA representative values for the given board. A board value + * of (-1) indicates all boards. + */ +/*ARGSUSED*/ +void +pda_mem_sync(pda_handle_t ph, int board, int unit) +{ + post2obp_info_t *p2o = ((p2o_info_t *)ph)->p2o_ptr; + register int b; + + for (b = 0; b < MAX_SYSBDS; b++) { + if ((board != -1) && (board != b)) + continue; + + if (pda_board_present(ph, b)) { + uint64_t masr; + uint_t masr_value; + + masr = STARFIRE_MC_ASR_ADDR_BOARD(b); + masr_value = ldphysio(masr); + + p2o->p2o_bdminfo[b].bmda_adr = masr_value; + } + + if (board == b) + break; + } + + p2o_update_checksum(p2o); +} + +void +pda_get_busmask(pda_handle_t ph, short *amask, short *dmask) +{ + post2obp_info_t *p2o = ((p2o_info_t *)ph)->p2o_ptr; + + if (amask) + *amask = p2o ? p2o->p2o_abus_mask : 0; + + if (dmask) + *dmask = p2o ? p2o->p2o_dbus_mask : 0; +} + +int +pda_is_valid(pda_handle_t ph) +{ + post2obp_info_t *p2o = ((p2o_info_t *)ph)->p2o_ptr; + uint_t csum; + + if (p2o == NULL) + return (0); + + csum = p2o_calc_checksum(p2o); + + return (csum == p2o->p2o_csum); +} + +/* + * Post2obp support functions below here. Internal to PDA module. + * + * p2o_update_checksum + * + * Calculate checksum for post2obp structure and insert it so + * when POST reads it he'll be happy. + */ +static void +p2o_update_checksum(post2obp_info_t *p2o) +{ + uint_t new_csum; + + ASSERT(p2o); + + new_csum = p2o_calc_checksum(p2o); + p2o->p2o_csum = new_csum; +} + +static uint_t +p2o_calc_checksum(post2obp_info_t *p2o) +{ + int i, nchunks; + uint_t *csumptr; + uint_t p2o_size; + uint_t csum, o_csum; + + ASSERT(p2o != NULL); + + nchunks = p2o->p2o_memtotal.Memt_NumChunks; + p2o_size = sizeof (post2obp_info_t) + + ((nchunks - VAR_ARRAY_LEN) * sizeof (MemChunk_t)); + p2o_size /= sizeof (uint_t); + + o_csum = p2o->p2o_csum; + p2o->p2o_csum = 0; + csum = 0; + for (i = 0, csumptr = (uint_t *)p2o; i < p2o_size; i++) + csum += *csumptr++; + p2o->p2o_csum = o_csum; + + return (-csum); +} + +/* + * Sort the mchunk list in ascending order based on the + * Memc_StartAddress field. + * + * disclosure: This is based on the qsort() library routine. + */ +static void +p2o_mem_sort(post2obp_info_t *p2o) +{ + MemChunk_t *base; + int nchunks; + uint_t c1, c2; + char *min, *max; + register char c, *i, *j, *lo, *hi; + + ASSERT(p2o != NULL); + + nchunks = p2o->p2o_memtotal.Memt_NumChunks; + base = &p2o->p2o_mchunks[0]; + + /* ala qsort() */ + max = (char *)base + nchunks * sizeof (MemChunk_t); + hi = max; + for (j = lo = (char *)base; (lo += sizeof (MemChunk_t)) < hi; ) { + c1 = ((MemChunk_t *)j)->Memc_StartAddress; + c2 = ((MemChunk_t *)lo)->Memc_StartAddress; + if (c1 > c2) + j = lo; + } + if (j != (char *)base) { + for (i = (char *)base, + hi = (char *)base + sizeof (MemChunk_t); + /* CSTYLED */ + i < hi;) { + c = *j; + *j++ = *i; + *i++ = c; + } + } + for (min = (char *)base; + /* CSTYLED */ + (hi = min += sizeof (MemChunk_t)) < max;) { + do { + hi -= sizeof (MemChunk_t); + c1 = ((MemChunk_t *)hi)->Memc_StartAddress; + c2 = ((MemChunk_t *)min)->Memc_StartAddress; + } while (c1 > c2); + if ((hi += sizeof (MemChunk_t)) != min) { + for (lo = min + sizeof (MemChunk_t); + /* CSTYLED */ + --lo >= min;) { + c = *lo; + for (i = j = lo; + (j -= sizeof (MemChunk_t)) >= hi; + i = j) { + *i = *j; + } + *i = c; + } + } + } +} + +static void +p2o_mem_coalesce(post2obp_info_t *p2o) +{ + MemChunk_t *mc; + int nchunks, new_nchunks; + uint_t addr, size, naddr, nsize; + uint_t npages; + register int i, cp, ncp; + + ASSERT(p2o != NULL); + + nchunks = new_nchunks = p2o->p2o_memtotal.Memt_NumChunks; + mc = &p2o->p2o_mchunks[0]; + + for (cp = i = 0; i < (nchunks-1); i++, cp = ncp) { + ncp = cp + 1; + addr = mc[cp].Memc_StartAddress; + size = mc[cp].Memc_Size; + naddr = mc[ncp].Memc_StartAddress; + nsize = mc[ncp].Memc_Size; + + if ((addr + size) >= naddr) { + uint_t overlap; + + overlap = addr + size - naddr; + /* + * if (nsize < overlap) then + * next entry fits within the current + * entry so no need to update size. + */ + if (nsize >= overlap) { + size += nsize - overlap; + mc[cp].Memc_Size = size; + } + bcopy((char *)&mc[ncp+1], + (char *)&mc[ncp], + (nchunks - ncp - 1) * sizeof (MemChunk_t)); + ncp = cp; + new_nchunks--; + } + } + + npages = 0; + for (i = 0; i < new_nchunks; i++) + npages += p2o->p2o_mchunks[i].Memc_Size; + + p2o->p2o_memtotal.Memt_NumChunks = new_nchunks; + p2o->p2o_memtotal.Memt_NumPages = npages; +} + +/* + * Mapin the the cpu's post2obp structure. + */ +static post2obp_info_t * +cpu_p2o_mapin(int cpuid) +{ + uint64_t cpu_p2o_physaddr; + uint32_t cpu_p2o_offset; + caddr_t cvaddr; + uint_t num_pages; + pfn_t pfn; + + ASSERT(cpu_sgnblkp[cpuid] != NULL); + /* + * Construct the physical base address of the bbsram + * in PSI space associated with this cpu in question. + */ + cpu_p2o_offset = (uint32_t)cpu_sgnblkp[cpuid]->sigb_postconfig; + if (cpu_p2o_offset == 0) { + cmn_err(CE_WARN, + "cpu_p2o_mapin:%d: sigb_postconfig == NULL\n", + cpuid); + return (NULL); + } + cpu_p2o_physaddr = (STARFIRE_UPAID2UPS(cpuid) | STARFIRE_PSI_BASE) + + (uint64_t)cpu_p2o_offset; + cpu_p2o_offset = (uint32_t)(cpu_p2o_physaddr & MMU_PAGEOFFSET); + cpu_p2o_physaddr -= (uint64_t)cpu_p2o_offset; + + /* + * cpu_p2o_physaddr = Beginning of page containing p2o. + * cpu_p2o_offset = Offset within page where p2o starts. + */ + + pfn = (pfn_t)(cpu_p2o_physaddr >> MMU_PAGESHIFT); + + num_pages = mmu_btopr(cpu_p2o_offset + sizeof (post2obp_info_t)); + + /* + * Map in the post2obp structure. + */ + cvaddr = vmem_alloc(heap_arena, ptob(num_pages), VM_SLEEP); + + hat_devload(kas.a_hat, cvaddr, ptob(num_pages), + pfn, PROT_READ | PROT_WRITE, HAT_LOAD_LOCK); + + return ((post2obp_info_t *)(cvaddr + (ulong_t)cpu_p2o_offset)); +} + +static void +cpu_p2o_mapout(int cpuid, post2obp_info_t *p2o) +{ + ulong_t cvaddr, num_pages; + uint32_t cpu_p2o_offset; + + ASSERT(cpu_sgnblkp[cpuid] != NULL); + + cpu_p2o_offset = (uint32_t)cpu_sgnblkp[cpuid]->sigb_postconfig; + if (cpu_p2o_offset == 0) { + cmn_err(CE_WARN, + "cpu_p2o_mapout:%d: sigb_postconfig == NULL\n", + cpuid); + return; + } + + cpu_p2o_offset = (uint32_t)(((STARFIRE_UPAID2UPS(cpuid) | + STARFIRE_PSI_BASE) + + (uint64_t)cpu_p2o_offset) & + MMU_PAGEOFFSET); + + num_pages = mmu_btopr(cpu_p2o_offset + sizeof (post2obp_info_t)); + + cvaddr = (ulong_t)p2o - cpu_p2o_offset; + if (cvaddr & MMU_PAGEOFFSET) { + cmn_err(CE_WARN, + "cpu_p2o_mapout:%d: cvaddr (0x%x) not on page " + "boundary\n", + cpuid, (uint_t)cvaddr); + return; + } + + hat_unload(kas.a_hat, (caddr_t)cvaddr, ptob(num_pages), + HAT_UNLOAD_UNLOCK); + vmem_free(heap_arena, (caddr_t)cvaddr, ptob(num_pages)); +} |
