/* * 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 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _SYS_FCODE_H #define _SYS_FCODE_H #include #include #include #include #include #ifdef __cplusplus extern "C" { #endif /* * The FCode driver presents a private interface to the fcode * user level interpreter. This interface is subject to change * at any time and is only provided for use by the fcode interpreter. * * The user program opens the device, causing a new instance of * the driver to be cloned. This instance is specific to a specific * instance of a new device managed by the kernel and driver framework. * * The interpreter does an FC_GET_PARAMETERS ioctl to get the fcode * length, which can be mmap-ed (at offset 0) to provide access to a copy * of the device's fcode. * * The interpreter uses the FC_RUN_PRIV ioctl to request privileged * operations to be run by the driver. * * The interpreter sends an FC_VALIDATE ioctl to notify the * driver that it's done interpreting FCode to signify a normal * ending sequence when the interpreter later closes the device. * This way the driver can easily distinguish between the user * level interpreter failing and finishing normally, thus validating * the interpreters actions and the state it downloads to the driver. * The 'arg' value in the FC_VALIDATE ioctl is ignored, there * are no arguments to this ioctl. */ #define FCIOC (0xfc<<8) #define FC_GET_PARAMETERS (FCIOC | 1) #define FC_RUN_PRIV (FCIOC | 2) #define FC_VALIDATE (FCIOC | 3) #define FC_GET_MY_ARGS (FCIOC | 4) #define FC_GET_FCODE_DATA (FCIOC | 5) #define FC_SET_FCODE_ERROR (FCIOC | 6) #define FC_GET_MY_ARGS_BUFLEN 256 /* Max my-args length */ /* * FC_GET_PARAMETERS: Expected as the first ioctl after a successful * open and blocking read (the read returns 0 when there's something * to interpret). The ioctl arg is a pointer to an fc_parameters * data structure which is filled in by the driver with the fcode * len (if any) and unit address of the new device. * Offset 0 .. fcode len may be used as the offset to an mmap call to * provide access to a copy of the device fcode. The unit address is * returned as a NULL terminated string. */ struct fc_parameters { int32_t fcode_size; char unit_address[OBP_MAXPATHLEN]; int config_address; }; /* * FC_RUN_PRIV: The ioctl 'arg' is a pointer to an array of fc_cell_t's * in the following format: * * fc_cell_t[0]: Pointer to a NULL terminated string: service name * fc_cell_t[1]: Number of input arguments (Call this value 'A') * fc_cell_t[2]: Number of output result cells allocated (Call this val 'R') * fc_cell_t[3]: Error Cell (See below) * fc_cell_t[4]: Priv Violation Cell (non-zero if priv. violation) * fc_cell_t[5]: Argument cell[0] (Possibly none) * fc_cell_t[5 + 'A']: Result cell[0] (Possibly none) * * The array is variable sized, and must contain a minimum of 5 fc_cell_t's. * The size (in fc_cell_t's) is 5 + 'A' + 'R'. * * The argument cells are filled in by the caller. The result cells * (if any) and error cell are returned to the caller by the driver. * The error cell and priv violation cell are filled in and returned * to the caller by the driver. * * Error Cell Values: * * -1: The call itself failed (the service name was unknown). * * 0: No error (though the result cells may indicate results * that signify an error consistent with the service request.) * * Priv Violation Cell Values: * * 0: No priv violation * * -1: Executing the request caused a priv. violation. * For example, an rl@ from an address not mapped in * by the interpreter. */ #define FC_ERR_NONE fc_int2cell(0) #define FC_ERR_SVC_NAME fc_int2cell(-1) #define FC_PRIV_OK fc_intcell(0) #define FC_PRIV_ERROR fc_int2cell(-1) /* * Client interface template: * The actual number of arguments is nargs. * The actual number of results is nresults. * The variable array 'v' contains 'nargs + nresults' elements */ struct fc_client_interface { fc_cell_t svc_name; fc_cell_t nargs; fc_cell_t nresults; fc_cell_t error; fc_cell_t priv_error; fc_cell_t v[1]; /* variable array of args and results */ }; typedef struct fc_client_interface fc_ci_t; #define fc_arg(cp, i) (cp->v[(i)]) #define fc_result(cp, i) (cp->v[fc_cell2int(cp->nargs) + (i)]) #define FCC_FIXED_CELLS 5 /* * FC_GET_FCODE_DATA: This ioctl allows userland portion of the fcode * interpreter to get the fcode into a local buffer without having * to use mmap() interface. * This allows DR kernel cage memory to be relocated while this * fcode buffer is allocated. * * The ioctl arg is a pointer to an fc_fcode_info structure which * has the fcode_size field set with the expected fcode length. * The driver uses this field to validate correct size before using * copyout() to fill in the fcode_ptr buffer with fcode data. */ typedef struct fc_fcode_info { int32_t fcode_size; char *fcode_ptr; } fc_fcode_info_t; /* * The service name len (max) is limited by the size of a method name */ #define FC_SVC_NAME_LEN OBP_MAXPROPNAME /* * "Internally" generated service names ... */ #define FC_SVC_VALIDATE "sunos,validate" #define FC_SVC_INVALIDATE "sunos,invalidate" #define FC_SVC_EXIT "sunos,exit" #define FC_OPEN_METHOD "open" #define FC_CLOSE_METHOD "close" #define FC_FIND_FCODE "$find" /* * Property related group: * * sunos,get*proplen ( propname-cstr phandle -- proplen ) * sunos,get*prop ( propname-cstr buf phandle -- proplen ) * * sunos,property ( propname-cstr buf len phandle -- ) */ #define FC_GET_MY_PROPLEN "sunos,get-my-proplen" #define FC_GET_MY_PROP "sunos,get-my-prop" #define FC_GET_IN_PROPLEN "sunos,get-inherited-proplen" #define FC_GET_IN_PROP "sunos,get-inherited-prop" #define FC_GET_PKG_PROPLEN "sunos,get-package-proplen" #define FC_GET_PKG_PROP "sunos,get-package-prop" #define FC_CREATE_PROPERTY "sunos,property" /* * Register access and dma ... same as 1275 * * dma-map-in maps in a suitable aligned user address. */ #define FC_RL_FETCH "rl@" #define FC_RW_FETCH "rw@" #define FC_RB_FETCH "rb@" #define FC_RL_STORE "rl!" #define FC_RW_STORE "rw!" #define FC_RB_STORE "rb!" #define FC_MAP_IN "map-in" #define FC_MAP_OUT "map-out" #define FC_DMA_MAP_IN "dma-map-in" #define FC_DMA_MAP_OUT "dma-map-out" /* * PCI configuration space access methods ... same as pci binding */ #define FC_PCI_CFG_L_FETCH "config-l@" #define FC_PCI_CFG_W_FETCH "config-w@" #define FC_PCI_CFG_B_FETCH "config-b@" #define FC_PCI_CFG_L_STORE "config-l!" #define FC_PCI_CFG_W_STORE "config-w!" #define FC_PCI_CFG_B_STORE "config-b!" /* * Device node creation ... * * Create a new device with the given name, unit-address, parent.phandle * with a phandle that must have been previously allocated using * sunos,alloc-phandle. finish-device marks the device creation and * the creation of its properties as complete. (It's a signal to the * the OS that the node is now reasonably complete.) * * sunos,new-device ( name-cstr unit-addr-cstr parent.phandle phandle -- ) * finish-device ( phandle -- ) */ #define FC_NEW_DEVICE "sunos,new-device" #define FC_FINISH_DEVICE "sunos,finish-device" /* * Navigation and configuration: * * sunos,probe-address ( -- phys.lo ... ) * sunos,probe-space ( -- phys.hi ) * * sunos,ap-phandle ( -- ap.phandle ) * Return attachment point phandle * * sunos,parent ( child.phandle -- parent.phandle ) * * child ( parent.phandle -- child.phandle ) * peer ( phandle -- phandle.sibling ) * * sunos,alloc-phandle ( -- phandle ) * Allocates a unique phandle, not associated with the device tree * * sunos,config-child ( -- child.phandle ) * Return the phandle of the child being configured. */ #define FC_PROBE_ADDRESS "sunos,probe-address" #define FC_PROBE_SPACE "sunos,probe-space" #define FC_AP_PHANDLE "sunos,ap-phandle" #define FC_PARENT "sunos,parent" #define FC_CHILD_FCODE "child" #define FC_PEER_FCODE "peer" #define FC_ALLOC_PHANDLE "sunos,alloc-phandle" #define FC_CONFIG_CHILD "sunos,config-child" /* * Fcode Drop In Routines: * sunos,get_fcode_size ( cstr -- len ) * Returns the size in bytes of the Fcode for a given drop in. * sunos,get_fcode (cstr buf len -- status? ) * Returns the Fcode image for a given drop in. */ #define FC_GET_FCODE_SIZE "sunos,get-fcode-size" #define FC_GET_FCODE "sunos,get-fcode" /* * Values for fc_request 'error'. This has been moved from the _KERNEL * area to allow the FC_SET_FCODE_ERROR ioctl to use these values to * signal the kernel as to the disposition of the userland interpreter. * NOTE: Positive values are used to indicate a kernel error, * negative values are used to identify userland interpreter errors. */ #define FC_SUCCESS 0 /* FCode interpreted successfully */ #define FC_TIMEOUT 1 /* Timer expired */ #define FC_ERROR -1 /* Interpreter error */ #define FC_EXEC_FAILED -2 /* Interpreter failed to exec */ #define FC_NO_FCODE -3 /* Interpreter couldn't find fcode */ #define FC_FCODE_ABORT -4 /* Interpreter called exit(1) */ #define FC_ERROR_VALID(s) ((s) >= FC_FCODE_ABORT) && ((s) <= FC_TIMEOUT) /* * kernel internal data structures and interfaces * for the fcode interpreter. */ #if defined(_KERNEL) /* * PCI bus-specific arguments. * * We can't get the physical config address of the child from the * unit address, so we supply it here, along with the child's dip * as the bus specific argument to pci_ops_alloc_handle. */ struct pci_ops_bus_args { int32_t config_address; /* phys.hi config addr component */ }; /* * Define data structures for resource lists and handle management * * 'untyped' resources are managed by the provider. */ struct fc_dma_resource { void *virt; size_t len; ddi_dma_handle_t h; uint32_t devaddr; struct buf *bp; }; struct fc_map_resource { void *virt; size_t len; ddi_acc_handle_t h; void *regspec; }; struct fc_nodeid_resource { int nodeid; /* An allocated nodeid */ }; struct fc_contigious_resource { void *virt; size_t len; }; struct fc_untyped_resource { int utype; /* providers private type field */ void (*free)(void *); /* function to free the resource */ void *resource; /* Pointer to the resource */ }; typedef enum { RT_DMA = 0, RT_MAP, RT_NODEID, RT_CONTIGIOUS, RT_UNTYPED } fc_resource_type_t; struct fc_resource { struct fc_resource *next; fc_resource_type_t type; union { struct fc_dma_resource d; struct fc_map_resource m; struct fc_nodeid_resource n; struct fc_contigious_resource c; struct fc_untyped_resource r; } un; }; #define fc_dma_virt un.d.virt #define fc_dma_len un.d.len #define fc_dma_handle un.d.h #define fc_dma_devaddr un.d.devaddr #define fc_dma_bp un.d.bp #define fc_map_virt un.m.virt #define fc_map_len un.m.len #define fc_map_handle un.m.h #define fc_regspec un.m.regspec #define fc_nodeid_r un.n.nodeid #define fc_contig_virt un.c.virt #define fc_contig_len un.c.len #define fc_untyped_type un.r.utype #define fc_untyped_free un.r.free #define fc_untyped_r un.r.resource struct fc_phandle_entry { struct fc_phandle_entry *next; dev_info_t *dip; fc_phandle_t h; }; extern void fc_phandle_table_alloc(struct fc_phandle_entry **); extern void fc_phandle_table_free(struct fc_phandle_entry **); extern dev_info_t *fc_phandle_to_dip(struct fc_phandle_entry **, fc_phandle_t); extern fc_phandle_t fc_dip_to_phandle(struct fc_phandle_entry **, dev_info_t *); extern void fc_add_dip_to_phandle(struct fc_phandle_entry **, dev_info_t *, fc_phandle_t); /* * Structures and functions for managing our own subtree rooted * at the attachment point. The parent linkage is established * at node creation time. The 'downwards' linkage isn't established * until the node is bound. */ struct fc_device_tree { dev_info_t *dip; struct fc_device_tree *child; struct fc_device_tree *peer; }; void fc_add_child(dev_info_t *child, dev_info_t *parent, struct fc_device_tree *head); void fc_remove_child(dev_info_t *child, struct fc_device_tree *head); dev_info_t *fc_child_node(dev_info_t *parent, struct fc_device_tree *head); dev_info_t *fc_peer_node(dev_info_t *devi, struct fc_device_tree *head); struct fc_device_tree *fc_find_node(dev_info_t *, struct fc_device_tree *); void fc_create_device_tree(dev_info_t *ap, struct fc_device_tree **head); void fc_remove_device_tree(struct fc_device_tree **head); /* * Our handles represent a list of resources associated with an * attachment point. The handles chain, just as the ops functions * do, with the ops caller responsible for remembering the handle * of the ops function below it. NB: Externally, this data structure * is opaque. (Not all members may be present in each chained cookie.) * For example, the dtree head is valid in only a single instance * of a set of chained cookies, so use the access function to find it.) */ struct fc_resource_list { struct fc_resource *head; void *next_handle; /* next handle in chain */ dev_info_t *ap; /* Attachment point dip */ dev_info_t *child; /* Child being configured, if any */ dev_info_t *cdip; /* Current node, if any */ int cdip_state; /* node creation state - see below */ void *fcode; /* fcode kernel address */ size_t fcode_size; /* fcode size or zero */ char *unit_address; /* childs unit address */ char *my_args; /* initial setting for my-args */ void *bus_args; /* bus dependent arguments */ struct fc_phandle_entry *ptable; /* devinfo/phandle table */ struct fc_device_tree *dtree; /* Our subtree (leaf cookie only) */ }; typedef struct fc_resource_list *fco_handle_t; /* * Values for cdip_state: */ #define FC_CDIP_NOSTATE 0x00 /* No state - no nodes created */ #define FC_CDIP_STARTED 0x01 /* Node started - dip in cdip */ #define FC_CDIP_DONE 0x02 /* Node finished - last dip in cdip */ #define FC_CDIP_CONFIG 0x10 /* subtree configured */ /* * Functions to allocate handles for the fcode_interpreter. * * This function allocates a handle, used to store resources * associated with this fcode request including the address of * the mapped in and copied in fcode and it's size or NULL, 0 * if there is no fcode (the interpreter may look for a drop-in * driver if there is no fcode), the unit address of child and * bus specific arguments. For PCI, the bus specific arguments * include the child's prototype dip and the config address of * the child, which can't be derived from the unit address. * * The 'handle' returned also contains resource information * about any allocations of kernel resources that the fcode * may have created. Thus, the handle's life is the life * of the plug-in card and can't be released until the card * is removed. Upon release, the resources are released. */ extern fco_handle_t fc_ops_alloc_handle(dev_info_t *ap, dev_info_t *config_child, void *fcode, size_t fcode_size, char *unit_address, void *bus_args); extern fco_handle_t pci_fc_ops_alloc_handle(dev_info_t *ap, dev_info_t *config_child, void *fcode, size_t fcode_size, char *unit_address, struct pci_ops_bus_args *bus_args); extern fco_handle_t gp2_fc_ops_alloc_handle(dev_info_t *ap, dev_info_t *config_child, void *fcode, size_t fcode_size, char *unit_address, char *my_args); extern void pci_fc_ops_free_handle(fco_handle_t handle); extern void gp2_fc_ops_free_handle(fco_handle_t handle); extern void fc_ops_free_handle(fco_handle_t handle); extern struct fc_phandle_entry **fc_handle_to_phandle_head(fco_handle_t rp); struct fc_device_tree **fc_handle_to_dtree_head(fco_handle_t); struct fc_device_tree *fc_handle_to_dtree(fco_handle_t); /* * fc_ops_t is the main glue back to the framework and attachment point driver * for privileged driver operations. The framework/driver provides a pointer * to the fc_ops function to handle the request given in the args. The dip * and handle are passed back to the framework/driver to distinguish * requests, if necessary. The argument array is an array of fc_cell_t's * and is defined in fcode.h * * The ops function should return -1 to indicate that the service name is * unknown and return the value 0 to indicate that the service name was known * and processed (even if it failed). ops functions may chain, using the * return code to communicate if the current function handled the service * request. Using this technique, the driver can provide certain ops functions * and allow a framework ops function to handle standardized ops functions, * or work hand in hand with a framework function so both can handle an op. * If an ops function is not handled, thus returning -1 to the driver, the * driver will log an error noting the name of the service and return the * error to the caller. */ typedef int (fc_ops_t)(dev_info_t *, fco_handle_t, fc_ci_t *); extern fc_ops_t fc_ops; extern fc_ops_t pci_fc_ops; extern fc_ops_t gp2_fc_ops; /* * Internal structure used to enque an fcode request * The 'next' and 'busy' fields are protected by a mutex. * Thread synchronization is accomplished via use of the 'busy' field. */ struct fc_request { struct fc_request *next; /* Next in chain (private) */ int busy; /* Waiters flag (private; see below) */ int error; /* Interpreter return code (private) */ dev_info_t *ap_dip; /* Attachment point. ie: pci nexus */ fc_ops_t *ap_ops; /* driver's fcode ops function */ fco_handle_t handle; /* Caller's private identifier */ timeout_id_t timeout; /* Timeout identifier */ }; /* * Values for 'busy'. The requester initializes the field to FC_R_INIT (0), * then waits for it be set to FC_R_DONE. The framework sets it to * FC_R_BUSY while working on the request so it can distinguish between * an inactive and an active request. */ #define FC_R_INIT 0 /* initialized, on queue */ #define FC_R_BUSY 1 /* request is active, busy */ #define FC_R_DONE 2 /* request is done and may be deq'd */ /* * Function to call to invoke the fcode interpreter. * * This function will wait and return when the interpreter either * completes successfully or fails, returning pass/fail status as * the return code. Interim calls to the driver's ops function will * be made for both priv. ops and to create device nodes and properties. * * Calling this function will log a message to userland to request the * eventd to start the userland fcode interpreter process. The interpreter * opens /dev/fcode, which clones an instance of the driver, and then * waits in a 'read' until there's an active request. * XXX: For the prototype, we can start it manually or use an init.d script. * * 'ap' is the attachment point dip: that is, the driving parent's dev_info_t * ie: for pci devices, this will be the dip of the pci nexus. * * The 'handle' is provided for the caller, and can be used to * identify the request along with the attachment point dip, both * of which will be passed back to the driver's ops function. * The handle is allocated first by calling a bus-specific * _ops_handle_alloc function. * * ops functions may chain; an ops function should return -1 if * the call was not recognized, or 0 if the call was recognized. */ extern int fcode_interpreter(dev_info_t *, fc_ops_t *, fco_handle_t); /* * The fcode implementation uses this function to wait for and 'de-queue' * an fcode request. It's triggered by a 'read' request from the * userland interpreter. It uses a 'sig' form of waiting (cv_wait_sig), * so the interpreter can interrupt the read. */ extern struct fc_request *fc_get_request(void); /* * When the fcode implementation is finished servicing a request, it calls this * function to mark the request as done and to signal the originating thread * (now waiting in fcode_interpreter) that the request is done. */ extern void fc_finish_request(struct fc_request *); /* * The fcode implementation uses these functions to manage * resource items and resource lists ... */ extern void fc_add_resource(fco_handle_t, struct fc_resource *); extern void fc_rem_resource(fco_handle_t, struct fc_resource *); extern void fc_lock_resource_list(fco_handle_t); extern void fc_unlock_resource_list(fco_handle_t); /* * ops common and helper functions */ extern int fc_fail_op(dev_info_t *, fco_handle_t, fc_ci_t *); extern int fc_success_op(dev_info_t *, fco_handle_t, fc_ci_t *); extern int fc_syntax_error(fc_ci_t *, char *); extern int fc_priv_error(fc_ci_t *, char *); /* * Recharacterized ddi functions we need to define ... * * The only difference is we call through the attachment point driver, * as a proxy for the child that isn't yet attached. The ddi functions * optimize these functions by not necessarily calling through the * attachment point driver. */ int fc_ddi_dma_alloc_handle(dev_info_t *dip, ddi_dma_attr_t *attr, int (*waitfp)(caddr_t), caddr_t arg, ddi_dma_handle_t *handlep); int fc_ddi_dma_buf_bind_handle(ddi_dma_handle_t handle, struct buf *bp, uint_t flags, int (*waitfp)(caddr_t), caddr_t arg, ddi_dma_cookie_t *cookiep, uint_t *ccountp); int fc_ddi_dma_unbind_handle(ddi_dma_handle_t handle); void fc_ddi_dma_free_handle(ddi_dma_handle_t *handlep); int fc_ddi_dma_sync(ddi_dma_handle_t h, off_t o, size_t l, uint_t whom); /* * The ndi prop functions aren't appropriate for the interpreter. * We create byte-array, untyped properties. */ int fc_ndi_prop_update(dev_t, dev_info_t *, char *, uchar_t *, uint_t); /* * The setup and teardown parts of physio() */ int fc_physio_setup(struct buf **bpp, void *io_base, size_t io_len); void fc_physio_free(struct buf **bpp, void *io_base, size_t io_len); /* * debugging macros */ extern int fcode_debug; #define dcmn_err(level, args) if (fcode_debug >= level) cmn_err args #ifdef DEBUG void fc_debug(char *, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t); #define FC_DEBUG0(level, flag, s) if (fcode_debug >= level) \ fc_debug(s, 0, 0, 0, 0, 0) #define FC_DEBUG1(level, flag, fmt, a1) if (fcode_debug >= level) \ fc_debug(fmt, (uintptr_t)(a1), 0, 0, 0, 0); #define FC_DEBUG2(level, flag, fmt, a1, a2) if (fcode_debug >= level) \ fc_debug(fmt, (uintptr_t)(a1), (uintptr_t)(a2), 0, 0, 0); #define FC_DEBUG3(level, flag, fmt, a1, a2, a3) \ if (fcode_debug >= level) \ fc_debug(fmt, (uintptr_t)(a1), (uintptr_t)(a2), (uintptr_t)(a3), 0, 0); #else #define FC_DEBUG0(level, flag, s) #define FC_DEBUG1(level, flag, fmt, a1) #define FC_DEBUG2(level, flag, fmt, a1, a2) #define FC_DEBUG3(level, flag, fmt, a1, a2, a3) #endif #endif /* defined(_KERNEL) */ #ifdef __cplusplus } #endif #endif /* _SYS_FCODE_H */