1 /*
2 * Copyright 2004-2024, Haiku, Inc. All rights reserved.
3 * Copyright 2002-2003, Thomas Kurschel. All rights reserved.
4 * Distributed under the terms of the MIT License.
5 */
6
7 /*!
8 Creates temporary Scatter/Gather table if the peripheral
9 driver has provided a simple pointer only.
10 */
11
12 #include "scsi_internal.h"
13 #include "KernelExport_ext.h"
14
15 #include <string.h>
16 #include <iovec.h>
17 #include <slab/Slab.h>
18
19
20 static object_cache* sTempScatterGatherPool = NULL;
21
22
23 static bool
fill_temp_sg(scsi_ccb * ccb)24 fill_temp_sg(scsi_ccb *ccb)
25 {
26 status_t res;
27 scsi_bus_info *bus = ccb->bus;
28 uint32 dma_boundary = bus->dma_params.dma_boundary;
29 uint32 max_sg_block_size = bus->dma_params.max_sg_block_size;
30 uint32 max_sg_blocks = min_c(bus->dma_params.max_sg_blocks,
31 (uint32)MAX_TEMP_SG_FRAGMENTS);
32 iovec vec = {
33 ccb->data,
34 ccb->data_length
35 };
36 uint32 num_entries;
37 size_t mapped_len;
38 physical_entry *temp_sg = (physical_entry *)ccb->sg_list;
39
40 res = get_iovec_memory_map(&vec, 1, 0, ccb->data_length, temp_sg, max_sg_blocks,
41 &num_entries, &mapped_len);
42
43 if (res != B_OK) {
44 SHOW_ERROR(2, "cannot create temporary S/G list for IO request (%s)", strerror(res));
45 return false;
46 }
47
48 if (mapped_len != ccb->data_length)
49 goto too_complex;
50
51 if (dma_boundary != ~(uint32)0 || ccb->data_length > max_sg_block_size) {
52 // S/G list may not be controller-compatible:
53 // we have to split offending entries
54 SHOW_FLOW(3, "Checking violation of dma boundary 0x%" B_PRIx32
55 " and entry size 0x%" B_PRIx32, dma_boundary, max_sg_block_size);
56
57 for (uint32 cur_idx = 0; cur_idx < num_entries; ++cur_idx) {
58 addr_t max_len;
59
60 // calculate space upto next dma boundary crossing
61 max_len = (dma_boundary + 1) -
62 (temp_sg[cur_idx].address & dma_boundary);
63 // restrict size per sg item
64 max_len = min_c(max_len, (addr_t)max_sg_block_size);
65
66 SHOW_FLOW(4, "addr=%#" B_PRIxPHYSADDR ", size=%" B_PRIxPHYSADDR
67 ", max_len=%" B_PRIxADDR ", idx=%" B_PRId32 ", num=%"
68 B_PRIu32, temp_sg[cur_idx].address, temp_sg[cur_idx].size,
69 max_len, cur_idx, num_entries);
70
71 if (max_len < temp_sg[cur_idx].size) {
72 // split sg block
73 if (++num_entries > max_sg_blocks)
74 goto too_complex;
75
76 memmove(&temp_sg[cur_idx + 1], &temp_sg[cur_idx],
77 (num_entries - 1 - cur_idx) * sizeof(physical_entry));
78
79 temp_sg[cur_idx].size = max_len;
80 temp_sg[cur_idx + 1].address
81 = temp_sg[cur_idx + 1].address + max_len;
82 temp_sg[cur_idx + 1].size -= max_len;
83 }
84 }
85 }
86
87 ccb->sg_count = num_entries;
88
89 return true;
90
91 too_complex:
92 SHOW_ERROR( 2, "S/G list to complex for IO request (max %d entries)",
93 MAX_TEMP_SG_FRAGMENTS );
94
95 return false;
96 }
97
98
99 bool
create_temp_sg(scsi_ccb * ccb)100 create_temp_sg(scsi_ccb *ccb)
101 {
102 physical_entry *temp_sg;
103 status_t res;
104
105 SHOW_FLOW(3, "ccb=%p, data=%p, data_length=%" B_PRIu32, ccb, ccb->data,
106 ccb->data_length);
107
108 ccb->sg_list = temp_sg = (physical_entry*)object_cache_alloc(sTempScatterGatherPool, 0);
109 if (temp_sg == NULL) {
110 SHOW_ERROR0(2, "cannot allocate memory for IO request!");
111 return false;
112 }
113
114 res = lock_memory(ccb->data, ccb->data_length, B_DMA_IO
115 | ((ccb->flags & SCSI_DIR_MASK) == SCSI_DIR_IN ? B_READ_DEVICE : 0));
116
117 if (res != B_OK) {
118 SHOW_ERROR(2, "cannot lock memory for IO request (%s)", strerror(res));
119 goto err;
120 }
121
122 if (fill_temp_sg(ccb))
123 // this is the success path
124 return true;
125
126 unlock_memory(ccb->data, ccb->data_length, B_DMA_IO
127 | ((ccb->flags & SCSI_DIR_MASK) == SCSI_DIR_IN ? B_READ_DEVICE : 0));
128
129 err:
130 object_cache_free(sTempScatterGatherPool, temp_sg, 0);
131 return false;
132 }
133
134
135 void
cleanup_temp_sg(scsi_ccb * ccb)136 cleanup_temp_sg(scsi_ccb *ccb)
137 {
138 status_t res;
139
140 SHOW_FLOW(3, "ccb=%p, data=%p, data_length=%" B_PRId32,
141 ccb, ccb->data, ccb->data_length);
142
143 res = unlock_memory(ccb->data, ccb->data_length, B_DMA_IO
144 | ((ccb->flags & SCSI_DIR_MASK) == SCSI_DIR_IN ? B_READ_DEVICE : 0));
145
146 if (res != B_OK) {
147 SHOW_FLOW0(3, "Cannot unlock previously locked memory!");
148 panic("Cannot unlock previously locked memory!");
149 }
150
151 object_cache_free(sTempScatterGatherPool, (void*)ccb->sg_list, 0);
152
153 // restore previous state
154 ccb->sg_list = NULL;
155 }
156
157
158 //! #pragma mark - initialization/uninitialization
159
160
161 int
init_temp_sg(void)162 init_temp_sg(void)
163 {
164 sTempScatterGatherPool = create_object_cache("scsi temp s/g",
165 MAX_TEMP_SG_FRAGMENTS * sizeof(physical_entry), 0,
166 NULL, NULL, NULL);
167 if (sTempScatterGatherPool == NULL)
168 return B_NO_MEMORY;
169
170 object_cache_set_minimum_reserve(sTempScatterGatherPool, 1);
171 return B_OK;
172 }
173
174
175 void
uninit_temp_sg()176 uninit_temp_sg()
177 {
178 delete_object_cache(sTempScatterGatherPool);
179 }
180