112f080e7Smrj /* 212f080e7Smrj * CDDL HEADER START 312f080e7Smrj * 412f080e7Smrj * The contents of this file are subject to the terms of the 586c1f4dcSVikram Hegde * Common Development and Distribution License (the "License"). 686c1f4dcSVikram Hegde * You may not use this file except in compliance with the License. 712f080e7Smrj * 812f080e7Smrj * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 912f080e7Smrj * or http://www.opensolaris.org/os/licensing. 1012f080e7Smrj * See the License for the specific language governing permissions 1112f080e7Smrj * and limitations under the License. 1212f080e7Smrj * 1312f080e7Smrj * When distributing Covered Code, include this CDDL HEADER in each 1412f080e7Smrj * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 1512f080e7Smrj * If applicable, add the following below this CDDL HEADER, with the 1612f080e7Smrj * fields enclosed by brackets "[]" replaced with your own identifying 1712f080e7Smrj * information: Portions Copyright [yyyy] [name of copyright owner] 1812f080e7Smrj * 1912f080e7Smrj * CDDL HEADER END 2012f080e7Smrj */ 2112f080e7Smrj /* 22*ef4ab52fSFrank Van Der Linden * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. 2312f080e7Smrj */ 2412f080e7Smrj 2512f080e7Smrj #ifndef _SYS_ROOTNEX_H 2612f080e7Smrj #define _SYS_ROOTNEX_H 2712f080e7Smrj 2812f080e7Smrj /* 2912f080e7Smrj * x86 root nexus implementation specific state 3012f080e7Smrj */ 3112f080e7Smrj 3212f080e7Smrj #include <sys/types.h> 3312f080e7Smrj #include <sys/conf.h> 3412f080e7Smrj #include <sys/modctl.h> 3512f080e7Smrj #include <sys/sunddi.h> 3620906b23SVikram Hegde #include <sys/iommulib.h> 370b7ba611SMark Johnson #include <sys/sdt.h> 3812f080e7Smrj 3912f080e7Smrj #ifdef __cplusplus 4012f080e7Smrj extern "C" { 4112f080e7Smrj #endif 4212f080e7Smrj 4312f080e7Smrj 4412f080e7Smrj /* size of buffer used for ctlop reportdev */ 4512f080e7Smrj #define REPORTDEV_BUFSIZE 1024 4612f080e7Smrj 4712f080e7Smrj /* min and max interrupt vectors */ 4812f080e7Smrj #define VEC_MIN 1 4912f080e7Smrj #define VEC_MAX 255 5012f080e7Smrj 5112f080e7Smrj /* atomic increment/decrement to keep track of outstanding binds, etc */ 520b7ba611SMark Johnson #ifdef DEBUG 530b7ba611SMark Johnson #define ROOTNEX_DPROF_INC(addr) atomic_inc_64(addr) 540b7ba611SMark Johnson #define ROOTNEX_DPROF_DEC(addr) atomic_add_64(addr, -1) 550b7ba611SMark Johnson #define ROOTNEX_DPROBE1(name, type1, arg1) \ 560b7ba611SMark Johnson DTRACE_PROBE1(name, type1, arg1) 57*ef4ab52fSFrank Van Der Linden #define ROOTNEX_DPROBE2(name, type1, arg1, type2, arg2) \ 58*ef4ab52fSFrank Van Der Linden DTRACE_PROBE2(name, type1, arg1, type2, arg2) 590b7ba611SMark Johnson #define ROOTNEX_DPROBE3(name, type1, arg1, type2, arg2, type3, arg3) \ 600b7ba611SMark Johnson DTRACE_PROBE3(name, type1, arg1, type2, arg2, type3, arg3) 610b7ba611SMark Johnson #else 620b7ba611SMark Johnson #define ROOTNEX_DPROF_INC(addr) 630b7ba611SMark Johnson #define ROOTNEX_DPROF_DEC(addr) 640b7ba611SMark Johnson #define ROOTNEX_DPROBE1(name, type1, arg1) 65*ef4ab52fSFrank Van Der Linden #define ROOTNEX_DPROBE2(name, type1, arg1, type2, arg2) 660b7ba611SMark Johnson #define ROOTNEX_DPROBE3(name, type1, arg1, type2, arg2, type3, arg3) 670b7ba611SMark Johnson #endif 6812f080e7Smrj 6912f080e7Smrj /* set in dmac_type to signify that this cookie uses the copy buffer */ 7012f080e7Smrj #define ROOTNEX_USES_COPYBUF 0x80000000 7112f080e7Smrj 7212f080e7Smrj /* 7312f080e7Smrj * integer or boolean property name and value. A few static rootnex properties 7412f080e7Smrj * are created during rootnex attach from an array of rootnex_intprop_t.. 7512f080e7Smrj */ 7612f080e7Smrj typedef struct rootnex_intprop_s { 7712f080e7Smrj char *prop_name; 7812f080e7Smrj int prop_value; 7912f080e7Smrj } rootnex_intprop_t; 8012f080e7Smrj 8112f080e7Smrj /* 8212f080e7Smrj * sgl related information which is visible to rootnex_get_sgl(). Trying to 8312f080e7Smrj * isolate get_sgl() as much as possible so it can be easily replaced. 8412f080e7Smrj */ 8512f080e7Smrj typedef struct rootnex_sglinfo_s { 8612f080e7Smrj /* 8712f080e7Smrj * These are passed into rootnex_get_sgl(). 8812f080e7Smrj * 8912f080e7Smrj * si_min_addr - the minimum physical address 9012f080e7Smrj * si_max_addr - the maximum physical address 9112f080e7Smrj * si_max_cookie_size - the maximum size of a physically contiguous 9212f080e7Smrj * piece of memory that we can handle in a sgl. 9312f080e7Smrj * si_segmask - segment mask to determine if we cross a segment boundary 9407c6692fSMark Johnson * si_flags - dma_attr_flags 9512f080e7Smrj * si_max_pages - max number of pages this sgl could occupy (which 9612f080e7Smrj * is also the maximum number of cookies we might see. 9712f080e7Smrj */ 9812f080e7Smrj uint64_t si_min_addr; 9912f080e7Smrj uint64_t si_max_addr; 10012f080e7Smrj uint64_t si_max_cookie_size; 10112f080e7Smrj uint64_t si_segmask; 10207c6692fSMark Johnson uint_t si_flags; 10312f080e7Smrj uint_t si_max_pages; 10412f080e7Smrj 10512f080e7Smrj /* 10612f080e7Smrj * these are returned by rootnex_get_sgl() 10712f080e7Smrj * 10807c6692fSMark Johnson * si_bounce_on_seg - if we need to use bounce buffer for pages above 10907c6692fSMark Johnson * ddi_dma_seg 11012f080e7Smrj * si_copybuf_req - amount of copy buffer needed by the buffer. 11112f080e7Smrj * si_buf_offset - The initial offset into the first page of the buffer. 11212f080e7Smrj * It's set in get sgl and used in the bind slow path to help 11312f080e7Smrj * calculate the current page index & offset from the current offset 11412f080e7Smrj * which is relative to the start of the buffer. 11512f080e7Smrj * si_asp - address space of buffer passed in. 11612f080e7Smrj * si_sgl_size - The actual number of cookies in the sgl. This does 11712f080e7Smrj * not reflect and sharing that we might do on window boundaries. 11812f080e7Smrj */ 11907c6692fSMark Johnson boolean_t si_bounce_on_seg; 12012f080e7Smrj size_t si_copybuf_req; 12112f080e7Smrj off_t si_buf_offset; 12212f080e7Smrj struct as *si_asp; 12312f080e7Smrj uint_t si_sgl_size; 12412f080e7Smrj } rootnex_sglinfo_t; 12512f080e7Smrj 12612f080e7Smrj /* 12712f080e7Smrj * When we have to use the copy buffer, we allocate one of these structures per 12812f080e7Smrj * buffer page to track which pages need the copy buffer, what the kernel 12912f080e7Smrj * virtual address is (which the device can't reach), and what the copy buffer 13012f080e7Smrj * virtual address is (where the device dma's to/from). For 32-bit kernels, 13112f080e7Smrj * since we can't use seg kpm, we also need to keep the page_t around and state 13212f080e7Smrj * if we've currently mapped in the page into KVA space for buffers which don't 13312f080e7Smrj * have kva already and when we have multiple windows because we used up all our 13412f080e7Smrj * copy buffer space. 13512f080e7Smrj */ 13612f080e7Smrj typedef struct rootnex_pgmap_s { 13712f080e7Smrj boolean_t pm_uses_copybuf; 13812f080e7Smrj #if !defined(__amd64) 13912f080e7Smrj boolean_t pm_mapped; 14012f080e7Smrj page_t *pm_pp; 14112f080e7Smrj caddr_t pm_vaddr; 14212f080e7Smrj #endif 14312f080e7Smrj caddr_t pm_kaddr; 14412f080e7Smrj caddr_t pm_cbaddr; 14512f080e7Smrj } rootnex_pgmap_t; 14612f080e7Smrj 14712f080e7Smrj /* 14812f080e7Smrj * We only need to trim a buffer when we have multiple windows. Each window has 14912f080e7Smrj * trim state. We might have trimmed the end of the previous window, leaving the 15012f080e7Smrj * first cookie of this window trimmed[tr_trim_first] (which basically means we 15112f080e7Smrj * won't start with a new cookie), or we might need to trim the end of the 15212f080e7Smrj * current window [tr_trim_last] (which basically means we won't end with a 15312f080e7Smrj * complete cookie). We keep the same state for the first & last cookie in a 15412f080e7Smrj * window (a window can have one or more cookies). However, when we trim the 15512f080e7Smrj * last cookie, we keep a pointer to the last cookie in the trim state since we 15612f080e7Smrj * only need this info when we trim. The pointer to the first cookie in the 15712f080e7Smrj * window is in the window state since we need to know what the first cookie in 15812f080e7Smrj * the window is in various places. 15912f080e7Smrj * 16012f080e7Smrj * If we do trim a cookie, we save away the physical address and size of the 16112f080e7Smrj * cookie so that we can over write the cookie when we switch windows (the 16212f080e7Smrj * space for a cookie which is in two windows is shared between the windows. 16312f080e7Smrj * We keep around the same information for the last page in a window. 16412f080e7Smrj * 16512f080e7Smrj * if we happened to trim on a page that uses the copy buffer, and that page 16612f080e7Smrj * is also in the middle of a window boundary because we have filled up the 16712f080e7Smrj * copy buffer, we need to remember the copy buffer address for both windows 16812f080e7Smrj * since the same page will have different copy buffer addresses in the two 16912f080e7Smrj * windows. We need to due the same for kaddr in the 32-bit kernel since we 17012f080e7Smrj * have a limited kva space which we map to. 17112f080e7Smrj */ 17212f080e7Smrj typedef struct rootnex_trim_s { 17312f080e7Smrj boolean_t tr_trim_first; 17412f080e7Smrj boolean_t tr_trim_last; 17512f080e7Smrj ddi_dma_cookie_t *tr_last_cookie; 17612f080e7Smrj uint64_t tr_first_paddr; 17712f080e7Smrj uint64_t tr_last_paddr; 17812f080e7Smrj size_t tr_first_size; 17912f080e7Smrj size_t tr_last_size; 18012f080e7Smrj 18112f080e7Smrj boolean_t tr_first_copybuf_win; 18212f080e7Smrj boolean_t tr_last_copybuf_win; 18312f080e7Smrj uint_t tr_first_pidx; 18412f080e7Smrj uint_t tr_last_pidx; 18512f080e7Smrj caddr_t tr_first_cbaddr; 18612f080e7Smrj caddr_t tr_last_cbaddr; 18712f080e7Smrj #if !defined(__amd64) 18812f080e7Smrj caddr_t tr_first_kaddr; 18912f080e7Smrj caddr_t tr_last_kaddr; 19012f080e7Smrj #endif 19112f080e7Smrj } rootnex_trim_t; 19212f080e7Smrj 19312f080e7Smrj /* 19412f080e7Smrj * per window state. A bound DMA handle can have multiple windows. Each window 19512f080e7Smrj * will have the following state. We track if this window needs to sync, 19612f080e7Smrj * the offset into the buffer where the window starts, the size of the window. 19712f080e7Smrj * a pointer to the first cookie in the window, the number of cookies in the 19812f080e7Smrj * window, and the trim state for the window. For the 32-bit kernel, we keep 19912f080e7Smrj * track of if we need to remap the copy buffer when we switch to a this window 20012f080e7Smrj */ 20112f080e7Smrj typedef struct rootnex_window_s { 20212f080e7Smrj boolean_t wd_dosync; 20312f080e7Smrj uint_t wd_cookie_cnt; 20412f080e7Smrj off_t wd_offset; 20512f080e7Smrj size_t wd_size; 20612f080e7Smrj ddi_dma_cookie_t *wd_first_cookie; 20712f080e7Smrj rootnex_trim_t wd_trim; 20812f080e7Smrj #if !defined(__amd64) 20912f080e7Smrj boolean_t wd_remap_copybuf; 21012f080e7Smrj #endif 21112f080e7Smrj } rootnex_window_t; 21212f080e7Smrj 2133a634bfcSVikram Hegde typedef struct dvcookie { 2143a634bfcSVikram Hegde uint64_t dvck_dvma; 2153a634bfcSVikram Hegde uint64_t dvck_npages; 2163a634bfcSVikram Hegde } dvcookie_t; 2173a634bfcSVikram Hegde 2183a634bfcSVikram Hegde typedef struct dcookie { 2193a634bfcSVikram Hegde paddr_t dck_paddr; 2203a634bfcSVikram Hegde uint64_t dck_npages; 2213a634bfcSVikram Hegde } dcookie_t; 2223a634bfcSVikram Hegde 22312f080e7Smrj /* per dma handle private state */ 22412f080e7Smrj typedef struct rootnex_dma_s { 22512f080e7Smrj /* 22612f080e7Smrj * sgl related state used to build and describe the sgl. 22712f080e7Smrj * 22812f080e7Smrj * dp_partial_required - used in the bind slow path to identify if we 22912f080e7Smrj * need to do a partial mapping or not. 23012f080e7Smrj * dp_trim_required - used in the bind slow path to identify if we 23112f080e7Smrj * need to trim when switching to a new window. This should only be 23212f080e7Smrj * set when partial is set. 23312f080e7Smrj * dp_granularity_power_2 - set in alloc handle and used in bind slow 23412f080e7Smrj * path to determine if we & or % to calculate the trim. 23512f080e7Smrj * dp_dma - copy of dma "object" passed in during bind 23612f080e7Smrj * dp_maxxfer - trimmed dma_attr_maxxfer so that it is a whole 23712f080e7Smrj * multiple of granularity 23812f080e7Smrj * dp_sglinfo - See rootnex_sglinfo_t above. 23912f080e7Smrj */ 24012f080e7Smrj boolean_t dp_partial_required; 24112f080e7Smrj boolean_t dp_trim_required; 24212f080e7Smrj boolean_t dp_granularity_power_2; 24312f080e7Smrj uint64_t dp_maxxfer; 24412f080e7Smrj ddi_dma_obj_t dp_dma; 24512f080e7Smrj rootnex_sglinfo_t dp_sglinfo; 24612f080e7Smrj 24712f080e7Smrj /* 24812f080e7Smrj * Copy buffer related state 24912f080e7Smrj * 25012f080e7Smrj * dp_copybuf_size - the actual size of the copy buffer that we are 25112f080e7Smrj * using. This can be smaller that dp_copybuf_req, i.e. bind size > 25212f080e7Smrj * max copy buffer size. 25312f080e7Smrj * dp_cbaddr - kernel address of copy buffer. Used to determine where 25412f080e7Smrj * where to copy to/from. 25512f080e7Smrj * dp_cbsize - the "real" size returned from the copy buffer alloc. 25612f080e7Smrj * Set in the copybuf alloc and used to free copybuf. 25712f080e7Smrj * dp_pgmap - page map used in sync to determine which pages in the 25812f080e7Smrj * buffer use the copy buffer and what addresses to use to copy to/ 25912f080e7Smrj * from. 26012f080e7Smrj * dp_cb_remaping - status if this bind causes us to have to remap 26112f080e7Smrj * the copybuf when switching to new windows. This is only used in 26212f080e7Smrj * the 32-bit kernel since we use seg kpm in the 64-bit kernel for 26312f080e7Smrj * this case. 26412f080e7Smrj * dp_kva - kernel heap arena vmem space for mapping to buffers which 26512f080e7Smrj * we don't have a kernel VA to bcopy to/from. This is only used in 26612f080e7Smrj * the 32-bit kernel since we use seg kpm in the 64-bit kernel for 26712f080e7Smrj * this case. 26812f080e7Smrj */ 26912f080e7Smrj size_t dp_copybuf_size; 27012f080e7Smrj caddr_t dp_cbaddr; 27112f080e7Smrj size_t dp_cbsize; 27212f080e7Smrj rootnex_pgmap_t *dp_pgmap; 27312f080e7Smrj #if !defined(__amd64) 27412f080e7Smrj boolean_t dp_cb_remaping; 27512f080e7Smrj caddr_t dp_kva; 27612f080e7Smrj #endif 27712f080e7Smrj 27812f080e7Smrj /* 27912f080e7Smrj * window related state. The pointer to the window state array which may 28012f080e7Smrj * be a pointer into the pre allocated state, or we may have had to 28112f080e7Smrj * allocate the window array on the fly because it wouldn't fit. If 28212f080e7Smrj * we allocate it, we'll use dp_need_to_free_window and dp_window_size 28312f080e7Smrj * during cleanup. dp_current_win keeps track of the current window. 28412f080e7Smrj * dp_max_win is the maximum number of windows we could have. 28512f080e7Smrj */ 28612f080e7Smrj uint_t dp_current_win; 28712f080e7Smrj rootnex_window_t *dp_window; 28812f080e7Smrj boolean_t dp_need_to_free_window; 28912f080e7Smrj uint_t dp_window_size; 29012f080e7Smrj uint_t dp_max_win; 29112f080e7Smrj 29212f080e7Smrj /* dip of driver which "owns" handle. set to rdip in alloc_handle() */ 29312f080e7Smrj dev_info_t *dp_dip; 29412f080e7Smrj 29512f080e7Smrj /* 29612f080e7Smrj * dp_mutex and dp_inuse are only used to see if a driver is trying to 29712f080e7Smrj * bind to an already bound dma handle. dp_mutex only used for dp_inuse 29812f080e7Smrj */ 29912f080e7Smrj kmutex_t dp_mutex; 30012f080e7Smrj boolean_t dp_inuse; 30112f080e7Smrj 30212f080e7Smrj /* 30312f080e7Smrj * cookie related state. The pointer to the cookies (dp_cookies) may 30412f080e7Smrj * be a pointer into the pre allocated state, or we may have had to 30512f080e7Smrj * allocate the cookie array on the fly because it wouldn't fit. If 30612f080e7Smrj * we allocate it, we'll use dp_need_to_free_cookie and dp_cookie_size 30712f080e7Smrj * during cleanup. dp_current_cookie is only used in the obsoleted 30812f080e7Smrj * interfaces to determine when we've used up all the cookies in a 30912f080e7Smrj * window during nextseg().. 31012f080e7Smrj */ 31112f080e7Smrj size_t dp_cookie_size; 31212f080e7Smrj ddi_dma_cookie_t *dp_cookies; 31312f080e7Smrj boolean_t dp_need_to_free_cookie; 31412f080e7Smrj uint_t dp_current_cookie; /* for obsoleted I/Fs */ 31594f1124eSVikram Hegde ddi_dma_cookie_t *dp_saved_cookies; 31694f1124eSVikram Hegde boolean_t dp_need_to_switch_cookies; 31712f080e7Smrj 31812f080e7Smrj /* 31912f080e7Smrj * pre allocated space for the bind state, allocated during alloc 32012f080e7Smrj * handle. For a lot of devices, this will save us from having to do 32112f080e7Smrj * kmem_alloc's during the bind most of the time. kmem_alloc's can be 32212f080e7Smrj * expensive on x86 when the cpu count goes up since xcalls are 32312f080e7Smrj * expensive on x86. 32412f080e7Smrj */ 32512f080e7Smrj uchar_t *dp_prealloc_buffer; 32686c1f4dcSVikram Hegde 32786c1f4dcSVikram Hegde /* 3283a634bfcSVikram Hegde * Intel IOMMU (immu) related state 3293a634bfcSVikram Hegde * dv_cookies saves the dvma allocated for this handler 3303a634bfcSVikram Hegde * max index of dvcookies in dvmax 33186c1f4dcSVikram Hegde */ 3323a634bfcSVikram Hegde dvcookie_t *dp_dvcookies; 3333a634bfcSVikram Hegde uint64_t dp_dvmax; 3343a634bfcSVikram Hegde dcookie_t *dp_dcookies; 3353a634bfcSVikram Hegde uint64_t dp_dmax; 3363a634bfcSVikram Hegde uint64_t dp_max_cookies; 3373a634bfcSVikram Hegde uint64_t dp_max_dcookies; 33894f1124eSVikram Hegde 33994f1124eSVikram Hegde /* 34094f1124eSVikram Hegde * sleep flags set on bind and unset on unbind 34194f1124eSVikram Hegde */ 34294f1124eSVikram Hegde int dp_sleep_flags; 34312f080e7Smrj } rootnex_dma_t; 34412f080e7Smrj 34512f080e7Smrj /* 34612f080e7Smrj * profile/performance counters. Most things will be dtrace probes, but there 34712f080e7Smrj * are a couple of things we want to keep track all the time. We track the 34812f080e7Smrj * total number of active handles and binds (i.e. an alloc without a free or 34912f080e7Smrj * a bind without an unbind) since rootnex attach. We also track the total 35012f080e7Smrj * number of binds which have failed since rootnex attach. 35112f080e7Smrj */ 35212f080e7Smrj typedef enum { 35312f080e7Smrj ROOTNEX_CNT_ACTIVE_HDLS = 0, 35412f080e7Smrj ROOTNEX_CNT_ACTIVE_BINDS = 1, 35512f080e7Smrj ROOTNEX_CNT_ALLOC_FAIL = 2, 35612f080e7Smrj ROOTNEX_CNT_BIND_FAIL = 3, 35712f080e7Smrj ROOTNEX_CNT_SYNC_FAIL = 4, 35812f080e7Smrj ROOTNEX_CNT_GETWIN_FAIL = 5, 35912f080e7Smrj 36012f080e7Smrj /* This one must be last */ 36112f080e7Smrj ROOTNEX_CNT_LAST 36212f080e7Smrj } rootnex_cnt_t; 36312f080e7Smrj 36412f080e7Smrj /* 36512f080e7Smrj * global driver state. 36612f080e7Smrj * r_dmahdl_cache - dma_handle kmem_cache 36712f080e7Smrj * r_dvma_call_list_id - ddi_set_callback() id 36812f080e7Smrj * r_peekpoke_mutex - serialize peeks and pokes. 36912f080e7Smrj * r_dip - rootnex dip 37012f080e7Smrj * r_reserved_msg_printed - ctlops reserve message threshold 37112f080e7Smrj * r_counters - profile/performance counters 37212f080e7Smrj */ 37312f080e7Smrj typedef struct rootnex_state_s { 37412f080e7Smrj uint_t r_prealloc_cookies; 37512f080e7Smrj uint_t r_prealloc_size; 37612f080e7Smrj kmem_cache_t *r_dmahdl_cache; 37712f080e7Smrj uintptr_t r_dvma_call_list_id; 37812f080e7Smrj kmutex_t r_peekpoke_mutex; 37912f080e7Smrj dev_info_t *r_dip; 3807aec1d6eScindi ddi_iblock_cookie_t r_err_ibc; 38112f080e7Smrj boolean_t r_reserved_msg_printed; 38212f080e7Smrj uint64_t r_counters[ROOTNEX_CNT_LAST]; 38320906b23SVikram Hegde iommulib_nexhandle_t r_iommulib_handle; 38412f080e7Smrj } rootnex_state_t; 38512f080e7Smrj 38612f080e7Smrj #ifdef __cplusplus 38712f080e7Smrj } 38812f080e7Smrj #endif 38912f080e7Smrj 39012f080e7Smrj #endif /* _SYS_ROOTNEX_H */ 391