#pragma ident "%Z%%M% %I% %E% SMI"
#include <sys/types.h>
#include <sys/systm.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/ddi_impldefs.h>
#include <sys/obpdefs.h>
#include <sys/errno.h>
#include <sys/kmem.h>
#include <sys/vmem.h>
#include <sys/debug.h>
#include <sys/sysmacros.h>
#include <sys/machsystm.h>
#include <sys/machparam.h>
#include <sys/modctl.h>
#include <sys/fhc.h>
#include <sys/ac.h>
#include <sys/vm.h>
#include <sys/cpu_module.h>
#include <vm/seg_kmem.h>
#include <vm/hat_sfmmu.h>
#include <sys/mem_config.h>
#include <sys/mem_cage.h>
int ac_add_clean = 1;
#define ADD_PAGESIZE MMU_PAGESIZE
ac_err_t
ac_kpm_err_cvt(int err)
{
switch (err) {
case KPHYSM_ESPAN:
return (AC_ERR_KPM_SPAN);
case KPHYSM_EFAULT:
return (AC_ERR_KPM_FAULT);
case KPHYSM_ERESOURCE:
return (AC_ERR_KPM_RESOURCE);
case KPHYSM_ENOTSUP:
return (AC_ERR_KPM_NOTSUP);
case KPHYSM_ENOHANDLES:
return (AC_ERR_KPM_NOHANDLES);
case KPHYSM_ENONRELOC:
return (AC_ERR_KPM_NONRELOC);
case KPHYSM_EHANDLE:
return (AC_ERR_KPM_HANDLE);
case KPHYSM_EBUSY:
return (AC_ERR_KPM_BUSY);
case KPHYSM_ENOTVIABLE:
return (AC_ERR_KPM_NOTVIABLE);
case KPHYSM_ESEQUENCE:
return (AC_ERR_KPM_SEQUENCE);
case KPHYSM_ENOWORK:
return (AC_ERR_KPM_NOWORK);
case KPHYSM_ECANCELLED:
return (AC_ERR_KPM_CANCELLED);
case KPHYSM_ENOTFINISHED:
return (AC_ERR_KPM_NOTFINISHED);
case KPHYSM_ENOTRUNNING:
return (AC_ERR_KPM_NOTRUNNING);
case KPHYSM_EREFUSED:
return (AC_ERR_KPM_REFUSED);
case KPHYSM_EDUP:
return (AC_ERR_KPM_DUP);
default:
return (AC_ERR_DEFAULT);
}
}
static int
ac_add_bank(struct bd_list *add, ac_cfga_pkt_t *pkt)
{
uint64_t decode;
uint64_t base_pa;
uint64_t limit_pa;
uint64_t current_pa;
int errs;
uint64_t bank_size;
struct ac_mem_info *mem_info;
struct ac_soft_state *asp = pkt->softsp;
uint_t ilv;
ilv = (pkt->bank == Bank0) ?
INTLV0(*asp->ac_memctl) : INTLV1(*asp->ac_memctl);
if (ilv != 1) {
AC_ERR_SET(pkt, AC_ERR_MEM_DEINTLV);
return (EINVAL);
}
decode = (pkt->bank == Bank0) ?
*asp->ac_memdecode0 : *asp->ac_memdecode1;
base_pa = GRP_REALBASE(decode);
bank_size = GRP_UK2SPAN(decode);
limit_pa = base_pa + bank_size;
mem_info = &asp->bank[pkt->bank];
if (ac_add_clean || mem_info->condition != SYSC_CFGA_COND_OK) {
caddr_t base_va;
caddr_t fill_buf;
int linesize;
base_va = vmem_alloc(heap_arena, PAGESIZE, VM_SLEEP);
fill_buf = kmem_zalloc(ADD_PAGESIZE, KM_SLEEP);
linesize = cpunodes[CPU->cpu_id].ecache_linesize;
kpreempt_disable();
for (current_pa = base_pa; current_pa < limit_pa;
current_pa += ADD_PAGESIZE) {
ac_mapin(current_pa, base_va);
ac_blkcopy(fill_buf, base_va,
ADD_PAGESIZE/linesize, linesize);
ac_unmap(base_va);
}
kpreempt_enable();
kmem_free(fill_buf, ADD_PAGESIZE);
vmem_free(heap_arena, base_va, PAGESIZE);
}
errs = kphysm_add_memory_dynamic(base_pa >> PAGESHIFT,
bank_size >> PAGESHIFT);
if (errs != KPHYSM_OK) {
AC_ERR_SET(pkt, ac_kpm_err_cvt(errs));
return (EINVAL);
}
errs = kcage_range_add(btop(base_pa), btop(bank_size), KCAGE_DOWN);
if (errs != 0)
cmn_err(CE_NOTE, "ac_add_bank(): board %d, bank %d, "
"kcage_range_add() returned %d",
add->sc.board, pkt->bank, errs);
return (0);
}
int
ac_add_memory(ac_cfga_pkt_t *pkt)
{
struct bd_list *board;
struct ac_mem_info *mem_info;
int force = pkt->cmd_cfga.force;
int retval;
board = fhc_bdlist_lock(pkt->softsp->board);
if (board == NULL || board->ac_softsp == NULL) {
fhc_bdlist_unlock();
AC_ERR_SET(pkt, AC_ERR_BD);
return (EINVAL);
}
ASSERT(pkt->softsp == board->ac_softsp);
switch (board->sc.type) {
case CPU_BOARD:
case MEM_BOARD:
break;
default:
fhc_bdlist_unlock();
AC_ERR_SET(pkt, AC_ERR_BD_TYPE);
return (EINVAL);
}
mem_info = &pkt->softsp->bank[pkt->bank];
if (!MEM_BOARD_VISIBLE(board) || mem_info->busy ||
fhc_bd_busy(pkt->softsp->board) ||
mem_info->rstate != SYSC_CFGA_RSTATE_CONNECTED ||
mem_info->ostate != SYSC_CFGA_OSTATE_UNCONFIGURED ||
(!force && mem_info->condition != SYSC_CFGA_COND_OK)) {
fhc_bdlist_unlock();
AC_ERR_SET(pkt, AC_ERR_BD_STATE);
return (EINVAL);
}
mem_info->busy = TRUE;
fhc_bdlist_unlock();
retval = ac_add_bank(board, pkt);
(void) fhc_bdlist_lock(-1);
mem_info->busy = FALSE;
if (retval == 0) {
mem_info->ostate = SYSC_CFGA_OSTATE_CONFIGURED;
mem_info->status_change = ddi_get_time();
}
fhc_bdlist_unlock();
if (retval != 0) {
return (retval);
}
return (DDI_SUCCESS);
}