/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (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 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * fcgp2.c: Framework gp2 (Safari) fcode ops */ #include #include #include #include #include #include #include #include #include #include #include #include #include static int gfc_map_in(dev_info_t *, fco_handle_t, fc_ci_t *); static int gfc_map_out(dev_info_t *, fco_handle_t, fc_ci_t *); static int gfc_register_fetch(dev_info_t *, fco_handle_t, fc_ci_t *); static int gfc_register_store(dev_info_t *, fco_handle_t, fc_ci_t *); static int gfc_claim_address(dev_info_t *, fco_handle_t, fc_ci_t *); static int gfc_claim_memory(dev_info_t *, fco_handle_t, fc_ci_t *); static int gfc_release_memory(dev_info_t *, fco_handle_t, fc_ci_t *); static int gfc_vtop(dev_info_t *, fco_handle_t, fc_ci_t *); static int gfc_master_intr(dev_info_t *, fco_handle_t, fc_ci_t *); static int gfc_config_child(dev_info_t *, fco_handle_t, fc_ci_t *); static int gfc_get_fcode_size(dev_info_t *, fco_handle_t, fc_ci_t *); static int gfc_get_fcode(dev_info_t *, fco_handle_t, fc_ci_t *); int prom_get_fcode_size(char *); int prom_get_fcode(char *, char *); int fcpci_unloadable; int no_advisory_dma; #define HIADDR(n) ((uint32_t)(((uint64_t)(n) & 0xFFFFFFFF00000000)>> 32)) #define LOADDR(n)((uint32_t)((uint64_t)(n) & 0x00000000FFFFFFFF)) #define LADDR(lo, hi) (((uint64_t)(hi) << 32) | (uint32_t)(lo)) #define PCI_4GIG_LIMIT 0xFFFFFFFFUL /* * Module linkage information for the kernel. */ static struct modlmisc modlmisc = { &mod_miscops, "FCode gp2 (safari) bus functions" }; static struct modlinkage modlinkage = { MODREV_1, (void *)&modlmisc, NULL }; int _init(void) { return (mod_install(&modlinkage)); } int _fini(void) { if (fcpci_unloadable) return (mod_remove(&modlinkage)); return (EBUSY); } int _info(struct modinfo *modinfop) { return (mod_info(&modlinkage, modinfop)); } struct gfc_ops_v { char *svc_name; fc_ops_t *f; }; struct gfc_ops_v gp2_pov[] = { { "map-in", gfc_map_in}, { "map-out", gfc_map_out}, { "rx@", gfc_register_fetch}, { "rl@", gfc_register_fetch}, { "rw@", gfc_register_fetch}, { "rb@", gfc_register_fetch}, { "rx!", gfc_register_store}, { "rl!", gfc_register_store}, { "rw!", gfc_register_store}, { "rb!", gfc_register_store}, { "claim-address", gfc_claim_address}, { "master-interrupt", gfc_master_intr}, { "claim-memory", gfc_claim_memory}, { "release-memory", gfc_release_memory}, { "vtop", gfc_vtop}, { FC_CONFIG_CHILD, gfc_config_child}, { FC_GET_FCODE_SIZE, gfc_get_fcode_size}, { FC_GET_FCODE, gfc_get_fcode}, { NULL, NULL} }; struct gfc_ops_v gp2_shared_pov[] = { { NULL, NULL} }; static int gp2_map_phys(dev_info_t *, struct regspec *, caddr_t *, ddi_device_acc_attr_t *, ddi_acc_handle_t *); static void gp2_unmap_phys(ddi_acc_handle_t *); fco_handle_t gp2_fc_ops_alloc_handle(dev_info_t *ap, dev_info_t *child, void *fcode, size_t fcode_size, char *unit_address, char *my_args) { fco_handle_t rp; phandle_t h; rp = kmem_zalloc(sizeof (struct fc_resource_list), KM_SLEEP); rp->next_handle = fc_ops_alloc_handle(ap, child, fcode, fcode_size, unit_address, NULL); rp->ap = ap; rp->child = child; rp->fcode = fcode; rp->fcode_size = fcode_size; rp->my_args = my_args; if (unit_address) { char *buf; buf = kmem_zalloc(strlen(unit_address) + 1, KM_SLEEP); (void) strcpy(buf, unit_address); rp->unit_address = buf; } /* * Add the child's nodeid to our table... */ h = ddi_get_nodeid(rp->child); fc_add_dip_to_phandle(fc_handle_to_phandle_head(rp), rp->child, h); return (rp); } void gp2_fc_ops_free_handle(fco_handle_t rp) { struct fc_resource *ip, *np; ASSERT(rp); if (rp->next_handle) fc_ops_free_handle(rp->next_handle); if (rp->unit_address) kmem_free(rp->unit_address, strlen(rp->unit_address) + 1); if (rp->my_args != NULL) kmem_free(rp->my_args, strlen(rp->my_args) + 1); /* * Release all the resources from the resource list */ for (ip = rp->head; ip != NULL; ip = np) { np = ip->next; switch (ip->type) { case RT_MAP: FC_DEBUG1(1, CE_CONT, "gp2_fc_ops_free: " " map handle - %p\n", ip->fc_map_handle); break; case RT_DMA: /* DMA has to be freed up at exit time */ cmn_err(CE_CONT, "gfc_fc_ops_free: DMA seen!\n"); break; case RT_CONTIGIOUS: FC_DEBUG2(1, CE_CONT, "gp2_fc_ops_free: " "Free claim-memory resource 0x%lx size 0x%x\n", ip->fc_contig_virt, ip->fc_contig_len); (void) ndi_ra_free(ddi_root_node(), (uint64_t)ip->fc_contig_virt, ip->fc_contig_len, "gptwo-contigousmem", NDI_RA_PASS); break; default: cmn_err(CE_CONT, "gp2_fc_ops_free: " "unknown resource type %d\n", ip->type); break; } fc_rem_resource(rp, ip); kmem_free(ip, sizeof (struct fc_resource)); } kmem_free(rp, sizeof (struct fc_resource_list)); } int gp2_fc_ops(dev_info_t *ap, fco_handle_t rp, fc_ci_t *cp) { struct gfc_ops_v *pv; char *name = fc_cell2ptr(cp->svc_name); ASSERT(rp); /* * First try the generic fc_ops. If the ops is a shared op, * also call our local function. */ if (fc_ops(ap, rp->next_handle, cp) == 0) { for (pv = gp2_shared_pov; pv->svc_name != NULL; ++pv) if (strcmp(pv->svc_name, name) == 0) return (pv->f(ap, rp, cp)); return (0); } for (pv = gp2_pov; pv->svc_name != NULL; ++pv) if (strcmp(pv->svc_name, name) == 0) return (pv->f(ap, rp, cp)); FC_DEBUG1(9, CE_CONT, "gp2_fc_ops: <%s> not serviced\n", name); return (-1); } /* * map-in (phys.lo phys.hi size -- virt ) */ static int gfc_map_in(dev_info_t *ap, fco_handle_t rp, fc_ci_t *cp) { size_t len; int error; caddr_t virt; struct fc_resource *ip; struct regspec r; ddi_device_acc_attr_t acc; ddi_acc_handle_t h; if (fc_cell2int(cp->nargs) != 3) return (fc_syntax_error(cp, "nargs must be 3")); if (fc_cell2int(cp->nresults) < 1) return (fc_syntax_error(cp, "nresults must be >= 1")); r.regspec_size = len = fc_cell2size(fc_arg(cp, 0)); r.regspec_bustype = fc_cell2uint(fc_arg(cp, 1)); r.regspec_addr = fc_cell2uint(fc_arg(cp, 2)); acc.devacc_attr_version = DDI_DEVICE_ATTR_V0; acc.devacc_attr_endian_flags = DDI_STRUCTURE_BE_ACC; acc.devacc_attr_dataorder = DDI_STRICTORDER_ACC; FC_DEBUG3(1, CE_CONT, "gfc_map_in: attempting map in " "address 0x%08x.%08x length %x\n", r.regspec_bustype, r.regspec_addr, r.regspec_size); error = gp2_map_phys(rp->child, &r, &virt, &acc, &h); if (error) { FC_DEBUG3(1, CE_CONT, "gfc_map_in: map in failed - " "address 0x%08x.%08x length %x\n", r.regspec_bustype, r.regspec_addr, r.regspec_size); return (fc_priv_error(cp, "gp2 map-in failed")); } FC_DEBUG1(3, CE_CONT, "gp2_map_in: returning virt %p\n", virt); cp->nresults = fc_int2cell(1); fc_result(cp, 0) = fc_ptr2cell(virt); /* * Log this resource ... */ ip = kmem_zalloc(sizeof (struct fc_resource), KM_SLEEP); ip->type = RT_MAP; ip->fc_map_virt = virt; ip->fc_map_len = len; ip->fc_map_handle = h; fc_add_resource(rp, ip); return (fc_success_op(ap, rp, cp)); } /* * map-out ( virt size -- ) */ static int gfc_map_out(dev_info_t *ap, fco_handle_t rp, fc_ci_t *cp) { caddr_t virt; size_t len; struct fc_resource *ip; if (fc_cell2int(cp->nargs) != 2) return (fc_syntax_error(cp, "nargs must be 2")); virt = fc_cell2ptr(fc_arg(cp, 1)); len = fc_cell2size(fc_arg(cp, 0)); FC_DEBUG2(1, CE_CONT, "gp2_map_out: attempting map out %p %x\n", virt, len); /* * Find if this request matches a mapping resource we set up. */ fc_lock_resource_list(rp); for (ip = rp->head; ip != NULL; ip = ip->next) { if (ip->type != RT_MAP) continue; if (ip->fc_map_virt != virt) continue; if (ip->fc_map_len == len) break; } fc_unlock_resource_list(rp); if (ip == NULL) return (fc_priv_error(cp, "request doesn't match a " "known mapping")); gp2_unmap_phys(&ip->fc_map_handle); /* * remove the resource from the list and release it. */ fc_rem_resource(rp, ip); kmem_free(ip, sizeof (struct fc_resource)); cp->nresults = fc_int2cell(0); return (fc_success_op(ap, rp, cp)); } static int gfc_register_fetch(dev_info_t *ap, fco_handle_t rp, fc_ci_t *cp) { size_t len; caddr_t virt; int error = 0; uint64_t x; uint32_t l; uint16_t w; uint8_t b; char *name = fc_cell2ptr(cp->svc_name); struct fc_resource *ip; if (fc_cell2int(cp->nargs) != 1) return (fc_syntax_error(cp, "nargs must be 1")); if (fc_cell2int(cp->nresults) < 1) return (fc_syntax_error(cp, "nresults must be >= 1")); virt = fc_cell2ptr(fc_arg(cp, 0)); /* * Determine the access width .. we can switch on the 2nd * character of the name which is "rx@", "rl@", "rb@" or "rw@" */ switch (*(name + 1)) { case 'x': len = sizeof (x); break; case 'l': len = sizeof (l); break; case 'w': len = sizeof (w); break; case 'b': len = sizeof (b); break; } /* * Check the alignment ... */ if (((intptr_t)virt & (len - 1)) != 0) return (fc_priv_error(cp, "unaligned access")); /* * Find if this virt is 'within' a request we know about */ fc_lock_resource_list(rp); for (ip = rp->head; ip != NULL; ip = ip->next) { if (ip->type == RT_MAP) { if ((virt >= (caddr_t)ip->fc_map_virt) && ((virt + len) <= ((caddr_t)ip->fc_map_virt + ip->fc_map_len))) break; } else if (ip->type == RT_CONTIGIOUS) { if ((virt >= (caddr_t)ip->fc_contig_virt) && ((virt + len) <= ((caddr_t)ip->fc_contig_virt + ip->fc_contig_len))) break; } } fc_unlock_resource_list(rp); if (ip == NULL) { return (fc_priv_error(cp, "request not within a " "known mapping or contigious adddress")); } switch (len) { case sizeof (x): if (ip->type == RT_MAP) error = ddi_peek64(rp->child, (int64_t *)virt, (int64_t *)&x); else /* RT_CONTIGIOUS */ x = *(int64_t *)virt; break; case sizeof (l): if (ip->type == RT_MAP) error = ddi_peek32(rp->child, (int32_t *)virt, (int32_t *)&l); else /* RT_CONTIGIOUS */ l = *(int32_t *)virt; break; case sizeof (w): if (ip->type == RT_MAP) error = ddi_peek16(rp->child, (int16_t *)virt, (int16_t *)&w); else /* RT_CONTIGIOUS */ w = *(int16_t *)virt; break; case sizeof (b): if (ip->type == RT_MAP) error = ddi_peek8(rp->child, (int8_t *)virt, (int8_t *)&b); else /* RT_CONTIGIOUS */ b = *(int8_t *)virt; break; } if (error) { FC_DEBUG2(1, CE_CONT, "gfc_register_fetch: access error " "accessing virt %p len %d\n", virt, len); return (fc_priv_error(cp, "access error")); } cp->nresults = fc_int2cell(1); switch (len) { case sizeof (x): fc_result(cp, 0) = x; break; case sizeof (l): fc_result(cp, 0) = fc_uint32_t2cell(l); break; case sizeof (w): fc_result(cp, 0) = fc_uint16_t2cell(w); break; case sizeof (b): fc_result(cp, 0) = fc_uint8_t2cell(b); break; } return (fc_success_op(ap, rp, cp)); } static int gfc_register_store(dev_info_t *ap, fco_handle_t rp, fc_ci_t *cp) { size_t len; caddr_t virt; uint64_t x; uint32_t l; uint16_t w; uint8_t b; char *name = fc_cell2ptr(cp->svc_name); struct fc_resource *ip; int error = 0; if (fc_cell2int(cp->nargs) != 2) return (fc_syntax_error(cp, "nargs must be 2")); virt = fc_cell2ptr(fc_arg(cp, 0)); /* * Determine the access width .. we can switch on the 2nd * character of the name which is "rx!", "rl!", "rb!" or "rw!" */ switch (*(name + 1)) { case 'x': len = sizeof (x); x = fc_arg(cp, 1); break; case 'l': len = sizeof (l); l = fc_cell2uint32_t(fc_arg(cp, 1)); break; case 'w': len = sizeof (w); w = fc_cell2uint16_t(fc_arg(cp, 1)); break; case 'b': len = sizeof (b); b = fc_cell2uint8_t(fc_arg(cp, 1)); break; } /* * Check the alignment ... */ if (((intptr_t)virt & (len - 1)) != 0) return (fc_priv_error(cp, "unaligned access")); /* * Find if this virt is 'within' a request we know about */ fc_lock_resource_list(rp); for (ip = rp->head; ip != NULL; ip = ip->next) { if (ip->type == RT_MAP) { if ((virt >= (caddr_t)ip->fc_map_virt) && ((virt + len) <= ((caddr_t)ip->fc_map_virt + ip->fc_map_len))) break; } else if (ip->type == RT_CONTIGIOUS) { if ((virt >= (caddr_t)ip->fc_contig_virt) && ((virt + len) <= ((caddr_t)ip->fc_contig_virt + ip->fc_contig_len))) break; } } fc_unlock_resource_list(rp); if (ip == NULL) return (fc_priv_error(cp, "request not within a " "known mapping or contigious address")); switch (len) { case sizeof (x): if (ip->type == RT_MAP) error = ddi_poke64(rp->child, (int64_t *)virt, x); else if (ip->type == RT_CONTIGIOUS) *(uint64_t *)virt = x; break; case sizeof (l): if (ip->type == RT_MAP) error = ddi_poke32(rp->child, (int32_t *)virt, l); else if (ip->type == RT_CONTIGIOUS) *(uint32_t *)virt = l; break; case sizeof (w): if (ip->type == RT_MAP) error = ddi_poke16(rp->child, (int16_t *)virt, w); else if (ip->type == RT_CONTIGIOUS) *(uint16_t *)virt = w; break; case sizeof (b): if (ip->type == RT_MAP) error = ddi_poke8(rp->child, (int8_t *)virt, b); else if (ip->type == RT_CONTIGIOUS) *(uint8_t *)virt = b; break; } if (error == DDI_FAILURE) { FC_DEBUG2(1, CE_CONT, "gfc_register_store: access error " "accessing virt %p len %d\n", virt, len); return (fc_priv_error(cp, "access error")); } cp->nresults = fc_int2cell(0); return (fc_success_op(ap, rp, cp)); } static int gfc_master_intr(dev_info_t *ap, fco_handle_t rp, fc_ci_t *cp) { int xt, portid; if (fc_cell2int(cp->nargs) != 2) return (fc_syntax_error(cp, "nargs must be 4")); if (fc_cell2int(cp->nresults) < 1) return (fc_syntax_error(cp, "nresults must be >= 1")); xt = fc_cell2int(fc_arg(cp, 1)); portid = fc_cell2int(fc_arg(cp, 0)); FC_DEBUG2(1, CE_CONT, "gfc_master_intr: reset-int-xt=%x portid=%x", xt, portid); cp->nresults = fc_int2cell(1); fc_result(cp, 0) = 0; return (fc_success_op(ap, rp, cp)); } /* * gfc_claim_address * * claim-address (size.lo size.hi type align bar portid -- base.lo base.hi ) */ static int gfc_claim_address(dev_info_t *ap, fco_handle_t rp, fc_ci_t *cp) { int bar, portid; uint64_t exp, slot, port, slice; uint64_t paddr; if (fc_cell2int(cp->nargs) != 6) return (fc_syntax_error(cp, "nargs must be 6")); if (fc_cell2int(cp->nresults) < 2) return (fc_syntax_error(cp, "nresults must be 2")); bar = fc_cell2int(fc_arg(cp, 1)); portid = fc_cell2int(fc_arg(cp, 0)); exp = portid >> 5; slot = (0x8 & portid) >> 3; port = portid & 0x1; switch (bar) { case 0: /* PCI IO Bus A */ paddr = (exp << 28) | (port << 26) | (slot << 27) | ((uint64_t)0x402 << 32); break; case 1: /* PCI Memory Bus A */ slice = (exp * 2) + slot + 1; paddr = ((uint64_t)1 << 42) | ((uint64_t)slice << 34) | ((uint64_t)port << 33); break; case 2: /* PCI IO Bus B */ paddr = (exp << 28) | (port << 26) | (slot << 27) | ((uint64_t)0x402 << 32) | (1 << 25); break; case 3: /* PCI Memory Bus B */ slice = (exp * 2) + slot + 1; paddr = ((uint64_t)1 << 42) | ((uint64_t)slice << 34) | ((uint64_t)port << 33); paddr |= ((uint64_t)1 << 32); break; default: cmn_err(CE_WARN, "gfc_claim_address - invalid BAR=0x%x\n", bar); return (fc_syntax_error(cp, "invalid argument")); } FC_DEBUG1(1, CE_CONT, "gfc_claim_address: returning 0x%lx\n", paddr); cp->nresults = fc_int2cell(2); fc_result(cp, 0) = LOADDR(paddr); fc_result(cp, 1) = HIADDR(paddr); return (fc_success_op(ap, rp, cp)); } /* * gfc_claim_memory * * claim-memory ( align size vhint -- vaddr) */ static int gfc_claim_memory(dev_info_t *ap, fco_handle_t rp, fc_ci_t *cp) { int align, size, vhint; ndi_ra_request_t request; uint64_t answer, alen; struct fc_resource *ip; if (fc_cell2int(cp->nargs) != 3) return (fc_syntax_error(cp, "nargs must be 3")); if (fc_cell2int(cp->nresults) < 1) return (fc_syntax_error(cp, "nresults must be >= 1")); vhint = fc_cell2int(fc_arg(cp, 2)); size = fc_cell2int(fc_arg(cp, 1)); align = fc_cell2int(fc_arg(cp, 0)); FC_DEBUG3(1, CE_CONT, "gfc_claim_memory: align=0x%x size=0x%x " "vhint=0x%x\n", align, size, vhint); if (size == 0) { cmn_err(CE_WARN, " gfc_claim_memory - unable to allocate " "contigiuos memory of size zero\n"); return (fc_priv_error(cp, "allocation error")); } if (vhint) { cmn_err(CE_WARN, "gfc_claim_memory - vhint is not zero " "vhint=0x%x - Ignoring Argument\n", vhint); } bzero((caddr_t)&request, sizeof (ndi_ra_request_t)); request.ra_flags = NDI_RA_ALLOC_BOUNDED; request.ra_boundbase = 0; request.ra_boundlen = 0xffffffff; request.ra_len = size; request.ra_align_mask = align - 1; if (ndi_ra_alloc(ddi_root_node(), &request, &answer, &alen, "gptwo-contigousmem", NDI_RA_PASS) != NDI_SUCCESS) { cmn_err(CE_WARN, " gfc_claim_memory - unable to allocate " "contigiuos memory\n"); return (fc_priv_error(cp, "allocation error")); } FC_DEBUG2(1, CE_CONT, "gfc_claim_memory: address allocated=0x%lx " "size=0x%x\n", answer, alen); cp->nresults = fc_int2cell(1); fc_result(cp, 0) = answer; /* * Log this resource ... */ ip = kmem_zalloc(sizeof (struct fc_resource), KM_SLEEP); ip->type = RT_CONTIGIOUS; ip->fc_contig_virt = (void *)answer; ip->fc_contig_len = size; fc_add_resource(rp, ip); return (fc_success_op(ap, rp, cp)); } /* * gfc_release_memory * * release-memory ( size vaddr -- ) */ static int gfc_release_memory(dev_info_t *ap, fco_handle_t rp, fc_ci_t *cp) { int32_t vaddr, size; struct fc_resource *ip; if (fc_cell2int(cp->nargs) != 2) return (fc_syntax_error(cp, "nargs must be 2")); if (fc_cell2int(cp->nresults) != 0) return (fc_syntax_error(cp, "nresults must be 0")); vaddr = fc_cell2int(fc_arg(cp, 1)); size = fc_cell2int(fc_arg(cp, 0)); FC_DEBUG2(1, CE_CONT, "gfc_release_memory: vaddr=0x%x size=0x%x\n", vaddr, size); /* * Find if this request matches a mapping resource we set up. */ fc_lock_resource_list(rp); for (ip = rp->head; ip != NULL; ip = ip->next) { if (ip->type != RT_CONTIGIOUS) continue; if (ip->fc_contig_virt != (void *)(uintptr_t)vaddr) continue; if (ip->fc_contig_len == size) break; } fc_unlock_resource_list(rp); if (ip == NULL) return (fc_priv_error(cp, "request doesn't match a " "known mapping")); (void) ndi_ra_free(ddi_root_node(), vaddr, size, "gptwo-contigousmem", NDI_RA_PASS); /* * remove the resource from the list and release it. */ fc_rem_resource(rp, ip); kmem_free(ip, sizeof (struct fc_resource)); cp->nresults = fc_int2cell(0); return (fc_success_op(ap, rp, cp)); } /* * gfc_vtop * * vtop ( vaddr -- paddr.lo paddr.hi) */ static int gfc_vtop(dev_info_t *ap, fco_handle_t rp, fc_ci_t *cp) { int vaddr; uint64_t paddr; struct fc_resource *ip; if (fc_cell2int(cp->nargs) != 1) return (fc_syntax_error(cp, "nargs must be 1")); if (fc_cell2int(cp->nresults) >= 3) return (fc_syntax_error(cp, "nresults must be less than 2")); vaddr = fc_cell2int(fc_arg(cp, 0)); /* * Find if this request matches a mapping resource we set up. */ fc_lock_resource_list(rp); for (ip = rp->head; ip != NULL; ip = ip->next) { if (ip->type != RT_CONTIGIOUS) continue; if (ip->fc_contig_virt == (void *)(uintptr_t)vaddr) break; } fc_unlock_resource_list(rp); if (ip == NULL) return (fc_priv_error(cp, "request doesn't match a " "known mapping")); paddr = va_to_pa((void *)(uintptr_t)vaddr); FC_DEBUG2(1, CE_CONT, "gfc_vtop: vaddr=0x%x paddr=0x%x\n", vaddr, paddr); cp->nresults = fc_int2cell(2); fc_result(cp, 0) = paddr; fc_result(cp, 1) = 0; return (fc_success_op(ap, rp, cp)); } static int gfc_config_child(dev_info_t *ap, fco_handle_t rp, fc_ci_t *cp) { fc_phandle_t h; if (fc_cell2int(cp->nargs) != 0) return (fc_syntax_error(cp, "nargs must be 0")); if (fc_cell2int(cp->nresults) < 1) return (fc_syntax_error(cp, "nresults must be >= 1")); h = fc_dip_to_phandle(fc_handle_to_phandle_head(rp), rp->child); cp->nresults = fc_int2cell(1); fc_result(cp, 0) = fc_phandle2cell(h); return (fc_success_op(ap, rp, cp)); } static int gfc_get_fcode(dev_info_t *ap, fco_handle_t rp, fc_ci_t *cp) { caddr_t name_virt, fcode_virt; char *name, *fcode; int fcode_len, status; if (fc_cell2int(cp->nargs) != 3) return (fc_syntax_error(cp, "nargs must be 3")); if (fc_cell2int(cp->nresults) < 1) return (fc_syntax_error(cp, "nresults must be >= 1")); name_virt = fc_cell2ptr(fc_arg(cp, 0)); fcode_virt = fc_cell2ptr(fc_arg(cp, 1)); fcode_len = fc_cell2int(fc_arg(cp, 2)); name = kmem_zalloc(FC_SVC_NAME_LEN, KM_SLEEP); if (copyinstr(fc_cell2ptr(name_virt), name, FC_SVC_NAME_LEN - 1, NULL)) { FC_DEBUG1(1, CE_CONT, "gfc_get_fcode: " "fault copying in drop in name %p\n", name_virt); status = 0; } else { fcode = kmem_zalloc(fcode_len, KM_SLEEP); if ((status = prom_get_fcode(name, fcode)) != 0) { if (copyout((void *)fcode, (void *)fcode_virt, fcode_len)) { cmn_err(CE_WARN, " gfc_get_fcode: Unable " "to copy out fcode image\n"); status = 0; } } kmem_free(fcode, fcode_len); } kmem_free(name, FC_SVC_NAME_LEN); cp->nresults = fc_int2cell(1); fc_result(cp, 0) = status; return (fc_success_op(ap, rp, cp)); } static int gfc_get_fcode_size(dev_info_t *ap, fco_handle_t rp, fc_ci_t *cp) { caddr_t virt; char *name; int len; if (fc_cell2int(cp->nargs) != 1) return (fc_syntax_error(cp, "nargs must be 1")); if (fc_cell2int(cp->nresults) < 1) return (fc_syntax_error(cp, "nresults must be >= 1")); virt = fc_cell2ptr(fc_arg(cp, 0)); name = kmem_zalloc(FC_SVC_NAME_LEN, KM_SLEEP); if (copyinstr(fc_cell2ptr(virt), name, FC_SVC_NAME_LEN - 1, NULL)) { FC_DEBUG1(1, CE_CONT, "gfc_get_fcode_size: " "fault copying in drop in name %p\n", virt); len = 0; } else { len = prom_get_fcode_size(name); } kmem_free(name, FC_SVC_NAME_LEN); cp->nresults = fc_int2cell(1); fc_result(cp, 0) = len; return (fc_success_op(ap, rp, cp)); } static int gp2_map_phys(dev_info_t *dip, struct regspec *phys_spec, caddr_t *addrp, ddi_device_acc_attr_t *accattrp, ddi_acc_handle_t *handlep) { ddi_map_req_t mr; ddi_acc_hdl_t *hp; int result; struct regspec *ph; *handlep = impl_acc_hdl_alloc(KM_SLEEP, NULL); hp = impl_acc_hdl_get(*handlep); hp->ah_vers = VERS_ACCHDL; hp->ah_dip = dip; hp->ah_rnumber = 0; hp->ah_offset = 0; hp->ah_len = 0; hp->ah_acc = *accattrp; ph = kmem_zalloc(sizeof (struct regspec), KM_SLEEP); *ph = *phys_spec; hp->ah_bus_private = ph; /* cache a copy of the reg spec */ mr.map_op = DDI_MO_MAP_LOCKED; mr.map_type = DDI_MT_REGSPEC; mr.map_obj.rp = (struct regspec *)phys_spec; mr.map_prot = PROT_READ | PROT_WRITE; mr.map_flags = DDI_MF_KERNEL_MAPPING; mr.map_handlep = hp; mr.map_vers = DDI_MAP_VERSION; result = ddi_map(dip, &mr, 0, 0, addrp); if (result != DDI_SUCCESS) { impl_acc_hdl_free(*handlep); *handlep = (ddi_acc_handle_t)NULL; } else { hp->ah_addr = *addrp; } return (result); } static void gp2_unmap_phys(ddi_acc_handle_t *handlep) { ddi_map_req_t mr; ddi_acc_hdl_t *hp; struct regspec_t *ph; hp = impl_acc_hdl_get(*handlep); ASSERT(hp); ph = hp->ah_bus_private; mr.map_op = DDI_MO_UNMAP; mr.map_type = DDI_MT_REGSPEC; mr.map_obj.rp = (struct regspec *)ph; mr.map_prot = PROT_READ | PROT_WRITE; mr.map_flags = DDI_MF_KERNEL_MAPPING; mr.map_handlep = hp; mr.map_vers = DDI_MAP_VERSION; (void) ddi_map(hp->ah_dip, &mr, hp->ah_offset, hp->ah_len, &hp->ah_addr); impl_acc_hdl_free(*handlep); kmem_free(ph, sizeof (struct regspec)); /* Free the cached copy */ *handlep = (ddi_acc_handle_t)NULL; }