11341Sstevel /*
21341Sstevel * CDDL HEADER START
31341Sstevel *
41341Sstevel * The contents of this file are subject to the terms of the
51341Sstevel * Common Development and Distribution License (the "License").
61341Sstevel * You may not use this file except in compliance with the License.
71341Sstevel *
81341Sstevel * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
91341Sstevel * or http://www.opensolaris.org/os/licensing.
101341Sstevel * See the License for the specific language governing permissions
111341Sstevel * and limitations under the License.
121341Sstevel *
131341Sstevel * When distributing Covered Code, include this CDDL HEADER in each
141341Sstevel * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
151341Sstevel * If applicable, add the following below this CDDL HEADER, with the
161341Sstevel * fields enclosed by brackets "[]" replaced with your own identifying
171341Sstevel * information: Portions Copyright [yyyy] [name of copyright owner]
181341Sstevel *
191341Sstevel * CDDL HEADER END
201341Sstevel */
211341Sstevel
221341Sstevel /*
23*4266Sdp78419 * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
241341Sstevel * Use is subject to license terms.
251341Sstevel */
261341Sstevel
271341Sstevel #pragma ident "%Z%%M% %I% %E% SMI"
281341Sstevel
291341Sstevel #include <sys/types.h>
301341Sstevel #include <sys/systm.h>
311341Sstevel #include <sys/ddi.h>
321341Sstevel #include <sys/sunddi.h>
331341Sstevel #include <sys/ddi_impldefs.h>
341341Sstevel #include <sys/obpdefs.h>
351341Sstevel #include <sys/errno.h>
361341Sstevel #include <sys/kmem.h>
371341Sstevel #include <sys/vmem.h>
381341Sstevel #include <sys/debug.h>
391341Sstevel #include <sys/sysmacros.h>
401341Sstevel #include <sys/machsystm.h>
411341Sstevel #include <sys/machparam.h>
421341Sstevel #include <sys/modctl.h>
431341Sstevel #include <sys/fhc.h>
441341Sstevel #include <sys/ac.h>
451341Sstevel #include <sys/vm.h>
461341Sstevel #include <sys/cpu_module.h>
471341Sstevel #include <vm/seg_kmem.h>
481341Sstevel #include <vm/hat_sfmmu.h>
491341Sstevel #include <sys/mem_config.h>
501341Sstevel #include <sys/mem_cage.h>
511341Sstevel
521341Sstevel /*
531341Sstevel * Default to always clean memory on add to reduce chance
541341Sstevel * of uncorrectable errors.
551341Sstevel */
561341Sstevel int ac_add_clean = 1;
571341Sstevel
581341Sstevel #define ADD_PAGESIZE MMU_PAGESIZE
591341Sstevel
601341Sstevel ac_err_t
ac_kpm_err_cvt(int err)611341Sstevel ac_kpm_err_cvt(int err)
621341Sstevel {
631341Sstevel switch (err) {
641341Sstevel case KPHYSM_ESPAN:
651341Sstevel return (AC_ERR_KPM_SPAN);
661341Sstevel case KPHYSM_EFAULT:
671341Sstevel return (AC_ERR_KPM_FAULT);
681341Sstevel case KPHYSM_ERESOURCE:
691341Sstevel return (AC_ERR_KPM_RESOURCE);
701341Sstevel case KPHYSM_ENOTSUP:
711341Sstevel return (AC_ERR_KPM_NOTSUP);
721341Sstevel case KPHYSM_ENOHANDLES:
731341Sstevel return (AC_ERR_KPM_NOHANDLES);
741341Sstevel case KPHYSM_ENONRELOC:
751341Sstevel return (AC_ERR_KPM_NONRELOC);
761341Sstevel case KPHYSM_EHANDLE:
771341Sstevel return (AC_ERR_KPM_HANDLE);
781341Sstevel case KPHYSM_EBUSY:
791341Sstevel return (AC_ERR_KPM_BUSY);
801341Sstevel case KPHYSM_ENOTVIABLE:
811341Sstevel return (AC_ERR_KPM_NOTVIABLE);
821341Sstevel case KPHYSM_ESEQUENCE:
831341Sstevel return (AC_ERR_KPM_SEQUENCE);
841341Sstevel case KPHYSM_ENOWORK:
851341Sstevel return (AC_ERR_KPM_NOWORK);
861341Sstevel case KPHYSM_ECANCELLED:
871341Sstevel return (AC_ERR_KPM_CANCELLED);
881341Sstevel case KPHYSM_ENOTFINISHED:
891341Sstevel return (AC_ERR_KPM_NOTFINISHED);
901341Sstevel case KPHYSM_ENOTRUNNING:
911341Sstevel return (AC_ERR_KPM_NOTRUNNING);
921341Sstevel case KPHYSM_EREFUSED:
931341Sstevel return (AC_ERR_KPM_REFUSED);
941341Sstevel case KPHYSM_EDUP:
951341Sstevel return (AC_ERR_KPM_DUP);
961341Sstevel default:
971341Sstevel return (AC_ERR_DEFAULT);
981341Sstevel }
991341Sstevel }
1001341Sstevel
1011341Sstevel static int
ac_add_bank(struct bd_list * add,ac_cfga_pkt_t * pkt)1021341Sstevel ac_add_bank(struct bd_list *add, ac_cfga_pkt_t *pkt)
1031341Sstevel {
1041341Sstevel uint64_t decode;
1051341Sstevel uint64_t base_pa;
1061341Sstevel uint64_t limit_pa;
1071341Sstevel uint64_t current_pa;
1081341Sstevel int errs;
1091341Sstevel uint64_t bank_size;
1101341Sstevel struct ac_mem_info *mem_info;
1111341Sstevel struct ac_soft_state *asp = pkt->softsp;
1121341Sstevel uint_t ilv;
1131341Sstevel
1141341Sstevel /*
1151341Sstevel * Cannot add interleaved banks at the moment.
1161341Sstevel */
1171341Sstevel ilv = (pkt->bank == Bank0) ?
1181341Sstevel INTLV0(*asp->ac_memctl) : INTLV1(*asp->ac_memctl);
1191341Sstevel if (ilv != 1) {
1201341Sstevel AC_ERR_SET(pkt, AC_ERR_MEM_DEINTLV);
1211341Sstevel return (EINVAL);
1221341Sstevel }
1231341Sstevel /*
1241341Sstevel * Determine the physical location of the selected bank
1251341Sstevel */
1261341Sstevel decode = (pkt->bank == Bank0) ?
1271341Sstevel *asp->ac_memdecode0 : *asp->ac_memdecode1;
1281341Sstevel base_pa = GRP_REALBASE(decode);
1291341Sstevel bank_size = GRP_UK2SPAN(decode);
1301341Sstevel limit_pa = base_pa + bank_size;
1311341Sstevel
1321341Sstevel mem_info = &asp->bank[pkt->bank];
1331341Sstevel if (ac_add_clean || mem_info->condition != SYSC_CFGA_COND_OK) {
1341341Sstevel caddr_t base_va;
1351341Sstevel caddr_t fill_buf;
1361341Sstevel int linesize;
1371341Sstevel
1381341Sstevel /*
1391341Sstevel * We need a page_va and a fill buffer for this operation
1401341Sstevel */
1411341Sstevel base_va = vmem_alloc(heap_arena, PAGESIZE, VM_SLEEP);
1421341Sstevel fill_buf = kmem_zalloc(ADD_PAGESIZE, KM_SLEEP);
1431341Sstevel linesize = cpunodes[CPU->cpu_id].ecache_linesize;
1441341Sstevel
1451341Sstevel /*
1461341Sstevel * zero fill the memory -- indirectly initializes the ECC
1471341Sstevel */
1481341Sstevel kpreempt_disable();
1491341Sstevel for (current_pa = base_pa; current_pa < limit_pa;
1501341Sstevel current_pa += ADD_PAGESIZE) {
1511341Sstevel
1521341Sstevel /* map current pa */
1531341Sstevel ac_mapin(current_pa, base_va);
1541341Sstevel
1551341Sstevel /* fill the target page */
1561341Sstevel ac_blkcopy(fill_buf, base_va,
1571341Sstevel ADD_PAGESIZE/linesize, linesize);
1581341Sstevel
1591341Sstevel /* tear down translation */
1601341Sstevel ac_unmap(base_va);
1611341Sstevel }
1621341Sstevel kpreempt_enable();
1631341Sstevel
1641341Sstevel /*
1651341Sstevel * clean up temporary resources
1661341Sstevel */
1671341Sstevel kmem_free(fill_buf, ADD_PAGESIZE);
1681341Sstevel vmem_free(heap_arena, base_va, PAGESIZE);
1691341Sstevel }
1701341Sstevel
1711341Sstevel /*
1721341Sstevel * give the memory to Solaris
1731341Sstevel */
1741341Sstevel errs = kphysm_add_memory_dynamic(base_pa >> PAGESHIFT,
1751341Sstevel bank_size >> PAGESHIFT);
1761341Sstevel
1771341Sstevel if (errs != KPHYSM_OK) {
1781341Sstevel AC_ERR_SET(pkt, ac_kpm_err_cvt(errs));
1791341Sstevel return (EINVAL);
1801341Sstevel }
1811341Sstevel
1821341Sstevel /*
1831341Sstevel * Add the board to the cage growth list.
1841341Sstevel */
185*4266Sdp78419 errs = kcage_range_add(btop(base_pa), btop(bank_size), KCAGE_DOWN);
1861341Sstevel /* TODO: deal with error return. */
1871341Sstevel if (errs != 0)
1881341Sstevel cmn_err(CE_NOTE, "ac_add_bank(): board %d, bank %d, "
1891341Sstevel "kcage_range_add() returned %d",
1901341Sstevel add->sc.board, pkt->bank, errs);
1911341Sstevel
1921341Sstevel return (0);
1931341Sstevel }
1941341Sstevel
1951341Sstevel int
ac_add_memory(ac_cfga_pkt_t * pkt)1961341Sstevel ac_add_memory(ac_cfga_pkt_t *pkt)
1971341Sstevel {
1981341Sstevel struct bd_list *board;
1991341Sstevel struct ac_mem_info *mem_info;
2001341Sstevel int force = pkt->cmd_cfga.force;
2011341Sstevel int retval;
2021341Sstevel
2031341Sstevel board = fhc_bdlist_lock(pkt->softsp->board);
2041341Sstevel if (board == NULL || board->ac_softsp == NULL) {
2051341Sstevel fhc_bdlist_unlock();
2061341Sstevel AC_ERR_SET(pkt, AC_ERR_BD);
2071341Sstevel return (EINVAL);
2081341Sstevel }
2091341Sstevel ASSERT(pkt->softsp == board->ac_softsp);
2101341Sstevel
2111341Sstevel /* verify the board is of the correct type */
2121341Sstevel switch (board->sc.type) {
2131341Sstevel case CPU_BOARD:
2141341Sstevel case MEM_BOARD:
2151341Sstevel break;
2161341Sstevel default:
2171341Sstevel fhc_bdlist_unlock();
2181341Sstevel AC_ERR_SET(pkt, AC_ERR_BD_TYPE);
2191341Sstevel return (EINVAL);
2201341Sstevel }
2211341Sstevel
2221341Sstevel /* verify the memory condition is acceptable */
2231341Sstevel mem_info = &pkt->softsp->bank[pkt->bank];
2241341Sstevel if (!MEM_BOARD_VISIBLE(board) || mem_info->busy ||
2251341Sstevel fhc_bd_busy(pkt->softsp->board) ||
2261341Sstevel mem_info->rstate != SYSC_CFGA_RSTATE_CONNECTED ||
2271341Sstevel mem_info->ostate != SYSC_CFGA_OSTATE_UNCONFIGURED ||
2281341Sstevel (!force && mem_info->condition != SYSC_CFGA_COND_OK)) {
2291341Sstevel fhc_bdlist_unlock();
2301341Sstevel AC_ERR_SET(pkt, AC_ERR_BD_STATE);
2311341Sstevel return (EINVAL);
2321341Sstevel }
2331341Sstevel
2341341Sstevel /*
2351341Sstevel * at this point, we have an available bank to add.
2361341Sstevel * mark it busy and initiate the add function.
2371341Sstevel */
2381341Sstevel mem_info->busy = TRUE;
2391341Sstevel fhc_bdlist_unlock();
2401341Sstevel
2411341Sstevel retval = ac_add_bank(board, pkt);
2421341Sstevel
2431341Sstevel /*
2441341Sstevel * We made it! Update the status and get out of here.
2451341Sstevel */
2461341Sstevel (void) fhc_bdlist_lock(-1);
2471341Sstevel mem_info->busy = FALSE;
2481341Sstevel if (retval == 0) {
2491341Sstevel mem_info->ostate = SYSC_CFGA_OSTATE_CONFIGURED;
2501341Sstevel mem_info->status_change = ddi_get_time();
2511341Sstevel }
2521341Sstevel
2531341Sstevel fhc_bdlist_unlock();
2541341Sstevel
2551341Sstevel if (retval != 0) {
2561341Sstevel return (retval);
2571341Sstevel }
2581341Sstevel return (DDI_SUCCESS);
2591341Sstevel }
260