1 /*
2 * This file and its contents are supplied under the terms of the
3 * Common Development and Distribution License ("CDDL"), version 1.0.
4 * You may only use this file in accordance with the terms of version
5 * 1.0 of the CDDL.
6 *
7 * A full copy of the text of the CDDL should have accompanied this
8 * source. A copy of the CDDL is also available via the Internet at
9 * http://www.illumos.org/license/CDDL.
10 */
11
12 /*
13 * Copyright 2024 Oxide Computer Company
14 */
15
16 #include "ena.h"
17
18 /*
19 * Create DMA attributes based on the conf parameter.
20 */
21 void
ena_dma_attr(const ena_t * ena,ddi_dma_attr_t * attrp,const ena_dma_conf_t * conf)22 ena_dma_attr(const ena_t *ena, ddi_dma_attr_t *attrp,
23 const ena_dma_conf_t *conf)
24 {
25 bzero(attrp, sizeof (*attrp));
26
27 /*
28 * Round up maximums to next page. This is what the Linux and
29 * FreeBSD driver do, so we follow suit.
30 */
31 const size_t size_up =
32 P2ROUNDUP_TYPED(conf->edc_size, ena->ena_page_sz, size_t);
33
34 attrp->dma_attr_version = DMA_ATTR_V0;
35
36 /*
37 * The device tells us the window it supports in terms of
38 * number of bits, we convert that to the appropriate mask.
39 */
40 ASSERT3U(ena->ena_dma_width, >=, 32);
41 ASSERT3U(ena->ena_dma_width, <=, 48);
42 attrp->dma_attr_addr_lo = 0x0;
43 attrp->dma_attr_addr_hi = ENA_DMA_BIT_MASK(ena->ena_dma_width);
44
45 /*
46 * This indicates the amount of data that can fit in one
47 * cookie/segment. We allow the entire object to live in one
48 * segment, when possible.
49 *
50 * NOTE: This value must be _one less_ than the desired max
51 * (i.e. a value of 4095 indicates a max of 4096).
52 */
53 attrp->dma_attr_count_max = size_up - 1;
54
55 /*
56 * The alignment of the starting address.
57 */
58 attrp->dma_attr_align = conf->edc_align;
59
60 /*
61 * The segment boundary dictates the address which a segment
62 * cannot cross. In this case there is no boundary.
63 */
64 attrp->dma_attr_seg = UINT64_MAX;
65
66 /*
67 * Allow a burst size of the entire object.
68 */
69 attrp->dma_attr_burstsizes = size_up;
70
71 /*
72 * Minimum and maximum amount of data we can send. This isn't
73 * strictly limited by PCI in hardware, as it'll just make the
74 * appropriate number of requests. Similarly, PCIe allows for
75 * an arbitrary granularity. We set this to one, as it's
76 * really a matter of what hardware is requesting from us.
77 */
78 attrp->dma_attr_minxfer = 0x1;
79 attrp->dma_attr_maxxfer = size_up;
80 attrp->dma_attr_granular = 0x1;
81
82 /*
83 * The maximum length of the Scatter Gather List, aka the
84 * maximum number of segments a device can address in a
85 * transfer.
86 */
87 attrp->dma_attr_sgllen = conf->edc_sgl;
88 }
89
90 void
ena_dma_free(ena_dma_buf_t * edb)91 ena_dma_free(ena_dma_buf_t *edb)
92 {
93 if (edb->edb_cookie != NULL) {
94 (void) ddi_dma_unbind_handle(edb->edb_dma_hdl);
95 edb->edb_cookie = NULL;
96 edb->edb_real_len = 0;
97 }
98
99 if (edb->edb_acc_hdl != NULL) {
100 ddi_dma_mem_free(&edb->edb_acc_hdl);
101 edb->edb_acc_hdl = NULL;
102 edb->edb_va = NULL;
103 }
104
105 if (edb->edb_dma_hdl != NULL) {
106 ddi_dma_free_handle(&edb->edb_dma_hdl);
107 edb->edb_dma_hdl = NULL;
108 }
109
110 edb->edb_va = NULL;
111 edb->edb_len = 0;
112 }
113
114 bool
ena_dma_alloc(ena_t * ena,ena_dma_buf_t * edb,ena_dma_conf_t * conf,size_t size)115 ena_dma_alloc(ena_t *ena, ena_dma_buf_t *edb, ena_dma_conf_t *conf, size_t size)
116 {
117 int ret;
118 size_t size_allocated;
119 ddi_dma_attr_t attr;
120 ddi_device_acc_attr_t acc;
121 uint_t flags =
122 conf->edc_stream ? DDI_DMA_STREAMING : DDI_DMA_CONSISTENT;
123
124 ena_dma_attr(ena, &attr, conf);
125
126 acc.devacc_attr_version = DDI_DEVICE_ATTR_V1;
127 acc.devacc_attr_endian_flags = conf->edc_endian;
128 acc.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
129
130 ret = ddi_dma_alloc_handle(ena->ena_dip, &attr, DDI_DMA_DONTWAIT, NULL,
131 &edb->edb_dma_hdl);
132 if (ret != DDI_SUCCESS) {
133 ena_err(ena, "!failed to allocate DMA handle: %d", ret);
134 return (false);
135 }
136
137 ret = ddi_dma_mem_alloc(edb->edb_dma_hdl, size, &acc, flags,
138 DDI_DMA_DONTWAIT, NULL, &edb->edb_va, &size_allocated,
139 &edb->edb_acc_hdl);
140 if (ret != DDI_SUCCESS) {
141 ena_err(ena, "!failed to allocate %lu bytes of DMA "
142 "memory: %d", size, ret);
143 ena_dma_free(edb);
144 return (false);
145 }
146
147 bzero(edb->edb_va, size_allocated);
148
149 ret = ddi_dma_addr_bind_handle(edb->edb_dma_hdl, NULL, edb->edb_va,
150 size_allocated, DDI_DMA_RDWR | flags, DDI_DMA_DONTWAIT, NULL, NULL,
151 NULL);
152 if (ret != DDI_SUCCESS) {
153 ena_err(ena, "!failed to bind %lu bytes of DMA "
154 "memory: %d", size_allocated, ret);
155 ena_dma_free(edb);
156 return (false);
157 }
158
159 edb->edb_len = size;
160 edb->edb_real_len = size_allocated;
161 edb->edb_cookie = ddi_dma_cookie_one(edb->edb_dma_hdl);
162 return (true);
163 }
164
165 void
ena_dma_bzero(ena_dma_buf_t * edb)166 ena_dma_bzero(ena_dma_buf_t *edb)
167 {
168 bzero(edb->edb_va, edb->edb_real_len);
169 }
170
171 /*
172 * Write the physical DMA address to the ENA hardware address pointer.
173 * While the DMA engine should guarantee that the allocation is within
174 * the specified range, we double check here to catch programmer error
175 * and avoid hard-to-debug situations.
176 */
177 void
ena_set_dma_addr(const ena_t * ena,const uint64_t phys_addr,enahw_addr_t * hwaddrp)178 ena_set_dma_addr(const ena_t *ena, const uint64_t phys_addr,
179 enahw_addr_t *hwaddrp)
180 {
181 ENA_DMA_VERIFY_ADDR(ena, phys_addr);
182 hwaddrp->ea_low = (uint32_t)phys_addr;
183 hwaddrp->ea_high = (uint16_t)(phys_addr >> 32);
184 }
185
186 /*
187 * The same as the above function, but writes the physical address to
188 * the supplied value pointers instead. Mostly used as a sanity check
189 * that the address fits in the reported DMA width.
190 */
191 void
ena_set_dma_addr_values(const ena_t * ena,const uint64_t phys_addr,uint32_t * dst_low,uint16_t * dst_high)192 ena_set_dma_addr_values(const ena_t *ena, const uint64_t phys_addr,
193 uint32_t *dst_low, uint16_t *dst_high)
194 {
195 ENA_DMA_VERIFY_ADDR(ena, phys_addr);
196 *dst_low = (uint32_t)phys_addr;
197 *dst_high = (uint16_t)(phys_addr >> 32);
198 }
199