1 | #include <uacpi/internal/opregion.h> |
2 | #include <uacpi/internal/namespace.h> |
3 | #include <uacpi/internal/utilities.h> |
4 | #include <uacpi/internal/helpers.h> |
5 | #include <uacpi/internal/log.h> |
6 | #include <uacpi/kernel_api.h> |
7 | #include <uacpi/uacpi.h> |
8 | |
9 | #define PCI_ROOT_PNP_ID "PNP0A03" |
10 | #define PCI_EXPRESS_ROOT_PNP_ID "PNP0A08" |
11 | |
12 | static uacpi_namespace_node *find_pci_root(uacpi_namespace_node *node) |
13 | { |
14 | static const uacpi_char *pci_root_ids[] = { |
15 | PCI_ROOT_PNP_ID, |
16 | PCI_EXPRESS_ROOT_PNP_ID, |
17 | UACPI_NULL |
18 | }; |
19 | uacpi_namespace_node *parent = node->parent; |
20 | |
21 | while (parent != uacpi_namespace_root()) { |
22 | if (uacpi_device_matches_pnp_id(node: parent, list: pci_root_ids)) { |
23 | uacpi_trace( |
24 | "found a PCI root node %.4s controlling region %.4s\n" , |
25 | parent->name.text, node->name.text |
26 | ); |
27 | return parent; |
28 | } |
29 | |
30 | parent = parent->parent; |
31 | } |
32 | |
33 | uacpi_trace_region_error( |
34 | node, message: "unable to find PCI root controlling" , |
35 | ret: UACPI_STATUS_NOT_FOUND |
36 | ); |
37 | return node; |
38 | } |
39 | |
40 | struct pci_region_ctx { |
41 | uacpi_pci_address address; |
42 | }; |
43 | |
44 | static uacpi_status pci_region_attach(uacpi_region_attach_data *data) |
45 | { |
46 | struct pci_region_ctx *ctx; |
47 | uacpi_namespace_node *node, *pci_root, *device; |
48 | uacpi_object *obj; |
49 | uacpi_status ret; |
50 | |
51 | ctx = uacpi_kernel_calloc(count: 1, size: sizeof(*ctx)); |
52 | if (ctx == UACPI_NULL) |
53 | return UACPI_STATUS_OUT_OF_MEMORY; |
54 | |
55 | node = data->region_node; |
56 | |
57 | pci_root = find_pci_root(node); |
58 | |
59 | /* |
60 | * Find the actual device object that is supposed to be controlling |
61 | * this operation region. |
62 | */ |
63 | device = node; |
64 | while (device) { |
65 | obj = uacpi_namespace_node_get_object(node: device); |
66 | if (obj && obj->type == UACPI_OBJECT_DEVICE) |
67 | break; |
68 | |
69 | device = device->parent; |
70 | } |
71 | |
72 | if (uacpi_unlikely(device == UACPI_NULL)) { |
73 | ret = UACPI_STATUS_NOT_FOUND; |
74 | uacpi_trace_region_error( |
75 | node, message: "unable to find device responsible for" , ret |
76 | ); |
77 | uacpi_free(ctx, sizeof(*ctx)); |
78 | return ret; |
79 | } |
80 | |
81 | ret = uacpi_eval_typed( |
82 | parent: device, path: "_ADR" , UACPI_NULL, |
83 | UACPI_OBJECT_INTEGER_BIT, ret: &obj |
84 | ); |
85 | if (ret == UACPI_STATUS_OK) { |
86 | ctx->address.function = (obj->integer >> 0) & 0xFF; |
87 | ctx->address.device = (obj->integer >> 16) & 0xFF; |
88 | uacpi_object_unref(obj); |
89 | } |
90 | |
91 | ret = uacpi_eval_typed( |
92 | parent: pci_root, path: "_SEG" , UACPI_NULL, |
93 | UACPI_OBJECT_INTEGER_BIT, ret: &obj |
94 | ); |
95 | if (ret == UACPI_STATUS_OK) { |
96 | ctx->address.segment = obj->integer; |
97 | uacpi_object_unref(obj); |
98 | } |
99 | |
100 | ret = uacpi_eval_typed( |
101 | parent: pci_root, path: "_BBN" , UACPI_NULL, |
102 | UACPI_OBJECT_INTEGER_BIT, ret: &obj |
103 | ); |
104 | if (ret == UACPI_STATUS_OK) { |
105 | ctx->address.bus = obj->integer; |
106 | uacpi_object_unref(obj); |
107 | } |
108 | |
109 | uacpi_trace( |
110 | "detected PCI device %.4s@%04X:%02X:%02X:%01X\n" , |
111 | device->name.text, ctx->address.segment, ctx->address.bus, |
112 | ctx->address.device, ctx->address.function |
113 | ); |
114 | |
115 | data->out_region_context = ctx; |
116 | return UACPI_STATUS_OK; |
117 | } |
118 | |
119 | static uacpi_status pci_region_detach(uacpi_region_detach_data *data) |
120 | { |
121 | struct pci_region_ctx *ctx = data->region_context; |
122 | |
123 | uacpi_free(ctx, sizeof(*ctx)); |
124 | return UACPI_STATUS_OK; |
125 | } |
126 | |
127 | static uacpi_status pci_region_do_rw( |
128 | uacpi_region_op op, uacpi_region_rw_data *data |
129 | ) |
130 | { |
131 | struct pci_region_ctx *ctx = data->region_context; |
132 | uacpi_u8 width; |
133 | uacpi_size offset; |
134 | |
135 | offset = data->offset; |
136 | width = data->byte_width; |
137 | |
138 | return op == UACPI_REGION_OP_READ ? |
139 | uacpi_kernel_pci_read(address: &ctx->address, offset, byte_width: width, value: &data->value) : |
140 | uacpi_kernel_pci_write(address: &ctx->address, offset, byte_width: width, value: data->value); |
141 | } |
142 | |
143 | static uacpi_status handle_pci_region(uacpi_region_op op, uacpi_handle op_data) |
144 | { |
145 | switch (op) { |
146 | case UACPI_REGION_OP_ATTACH: |
147 | return pci_region_attach(data: op_data); |
148 | case UACPI_REGION_OP_DETACH: |
149 | return pci_region_detach(data: op_data); |
150 | case UACPI_REGION_OP_READ: |
151 | case UACPI_REGION_OP_WRITE: |
152 | return pci_region_do_rw(op, data: op_data); |
153 | default: |
154 | return UACPI_STATUS_INVALID_ARGUMENT; |
155 | } |
156 | } |
157 | |
158 | struct memory_region_ctx { |
159 | uacpi_phys_addr phys; |
160 | uacpi_u8 *virt; |
161 | uacpi_size size; |
162 | }; |
163 | |
164 | static uacpi_status memory_region_attach(uacpi_region_attach_data *data) |
165 | { |
166 | struct memory_region_ctx *ctx; |
167 | uacpi_operation_region *op_region; |
168 | uacpi_status ret = UACPI_STATUS_OK; |
169 | |
170 | ctx = uacpi_kernel_alloc(size: sizeof(*ctx)); |
171 | if (ctx == UACPI_NULL) |
172 | return UACPI_STATUS_OUT_OF_MEMORY; |
173 | |
174 | op_region = uacpi_namespace_node_get_object(node: data->region_node)->op_region; |
175 | ctx->size = op_region->length; |
176 | |
177 | // FIXME: this really shouldn't try to map everything at once |
178 | ctx->phys = op_region->offset; |
179 | ctx->virt = uacpi_kernel_map(addr: ctx->phys, len: ctx->size); |
180 | |
181 | if (uacpi_unlikely(ctx->virt == UACPI_NULL)) { |
182 | ret = UACPI_STATUS_MAPPING_FAILED; |
183 | uacpi_trace_region_error(node: data->region_node, message: "unable to map" , ret); |
184 | uacpi_free(ctx, sizeof(*ctx)); |
185 | return ret; |
186 | } |
187 | |
188 | data->out_region_context = ctx; |
189 | return ret; |
190 | } |
191 | |
192 | static uacpi_status memory_region_detach(uacpi_region_detach_data *data) |
193 | { |
194 | struct memory_region_ctx *ctx = data->region_context; |
195 | |
196 | uacpi_kernel_unmap(addr: ctx->virt, len: ctx->size); |
197 | uacpi_free(ctx, sizeof(*ctx)); |
198 | return UACPI_STATUS_OK; |
199 | } |
200 | |
201 | struct io_region_ctx { |
202 | uacpi_io_addr base; |
203 | uacpi_handle handle; |
204 | }; |
205 | |
206 | static uacpi_status io_region_attach(uacpi_region_attach_data *data) |
207 | { |
208 | struct io_region_ctx *ctx; |
209 | uacpi_operation_region *op_region; |
210 | uacpi_status ret; |
211 | |
212 | ctx = uacpi_kernel_alloc(size: sizeof(*ctx)); |
213 | if (ctx == UACPI_NULL) |
214 | return UACPI_STATUS_OUT_OF_MEMORY; |
215 | |
216 | op_region = uacpi_namespace_node_get_object(node: data->region_node)->op_region; |
217 | ctx->base = op_region->offset; |
218 | |
219 | ret = uacpi_kernel_io_map(base: ctx->base, len: op_region->length, out_handle: &ctx->handle); |
220 | if (uacpi_unlikely_error(ret)) { |
221 | uacpi_trace_region_error( |
222 | node: data->region_node, message: "unable to map an IO" , ret |
223 | ); |
224 | uacpi_free(ctx, sizeof(*ctx)); |
225 | return ret; |
226 | } |
227 | |
228 | data->out_region_context = ctx; |
229 | return ret; |
230 | } |
231 | |
232 | static uacpi_status io_region_detach(uacpi_region_detach_data *data) |
233 | { |
234 | struct io_region_ctx *ctx = data->region_context; |
235 | |
236 | uacpi_kernel_io_unmap(handle: ctx->handle); |
237 | uacpi_free(ctx, sizeof(*ctx)); |
238 | return UACPI_STATUS_OK; |
239 | } |
240 | |
241 | static uacpi_status memory_read(void *ptr, uacpi_u8 width, uacpi_u64 *out) |
242 | { |
243 | switch (width) { |
244 | case 1: |
245 | *out = *(volatile uacpi_u8*)ptr; |
246 | break; |
247 | case 2: |
248 | *out = *(volatile uacpi_u16*)ptr; |
249 | break; |
250 | case 4: |
251 | *out = *(volatile uacpi_u32*)ptr; |
252 | break; |
253 | case 8: |
254 | *out = *(volatile uacpi_u64*)ptr; |
255 | break; |
256 | default: |
257 | return UACPI_STATUS_INVALID_ARGUMENT; |
258 | } |
259 | |
260 | return UACPI_STATUS_OK; |
261 | } |
262 | |
263 | static uacpi_status memory_write(void *ptr, uacpi_u8 width, uacpi_u64 in) |
264 | { |
265 | switch (width) { |
266 | case 1: |
267 | *(volatile uacpi_u8*)ptr = in; |
268 | break; |
269 | case 2: |
270 | *(volatile uacpi_u16*)ptr = in; |
271 | break; |
272 | case 4: |
273 | *(volatile uacpi_u32*)ptr = in; |
274 | break; |
275 | case 8: |
276 | *(volatile uacpi_u64*)ptr = in; |
277 | break; |
278 | default: |
279 | return UACPI_STATUS_INVALID_ARGUMENT; |
280 | } |
281 | |
282 | return UACPI_STATUS_OK; |
283 | } |
284 | |
285 | static uacpi_status memory_region_do_rw( |
286 | uacpi_region_op op, uacpi_region_rw_data *data |
287 | ) |
288 | { |
289 | struct memory_region_ctx *ctx = data->region_context; |
290 | uacpi_u8 *ptr; |
291 | |
292 | ptr = ctx->virt + (data->address - ctx->phys); |
293 | |
294 | return op == UACPI_REGION_OP_READ ? |
295 | memory_read(ptr, width: data->byte_width, out: &data->value) : |
296 | memory_write(ptr, width: data->byte_width, in: data->value); |
297 | } |
298 | |
299 | static uacpi_status handle_memory_region(uacpi_region_op op, uacpi_handle op_data) |
300 | { |
301 | switch (op) { |
302 | case UACPI_REGION_OP_ATTACH: |
303 | return memory_region_attach(data: op_data); |
304 | case UACPI_REGION_OP_DETACH: |
305 | return memory_region_detach(data: op_data); |
306 | case UACPI_REGION_OP_READ: |
307 | case UACPI_REGION_OP_WRITE: |
308 | return memory_region_do_rw(op, data: op_data); |
309 | default: |
310 | return UACPI_STATUS_INVALID_ARGUMENT; |
311 | } |
312 | } |
313 | |
314 | static uacpi_status table_data_region_do_rw( |
315 | uacpi_region_op op, uacpi_region_rw_data *data |
316 | ) |
317 | { |
318 | void *addr = UACPI_VIRT_ADDR_TO_PTR((uacpi_virt_addr)data->offset); |
319 | |
320 | return op == UACPI_REGION_OP_READ ? |
321 | memory_read(ptr: addr, width: data->byte_width, out: &data->value) : |
322 | memory_write(ptr: addr, width: data->byte_width, in: data->value); |
323 | } |
324 | |
325 | static uacpi_status handle_table_data_region(uacpi_region_op op, uacpi_handle op_data) |
326 | { |
327 | switch (op) { |
328 | case UACPI_REGION_OP_ATTACH: |
329 | case UACPI_REGION_OP_DETACH: |
330 | return UACPI_STATUS_OK; |
331 | case UACPI_REGION_OP_READ: |
332 | case UACPI_REGION_OP_WRITE: |
333 | return table_data_region_do_rw(op, data: op_data); |
334 | default: |
335 | return UACPI_STATUS_INVALID_ARGUMENT; |
336 | } |
337 | } |
338 | |
339 | static uacpi_status io_region_do_rw( |
340 | uacpi_region_op op, uacpi_region_rw_data *data |
341 | ) |
342 | { |
343 | struct io_region_ctx *ctx = data->region_context; |
344 | uacpi_u8 width; |
345 | uacpi_size offset; |
346 | |
347 | offset = data->offset - ctx->base; |
348 | width = data->byte_width; |
349 | |
350 | return op == UACPI_REGION_OP_READ ? |
351 | uacpi_kernel_io_read(ctx->handle, offset, byte_width: width, value: &data->value) : |
352 | uacpi_kernel_io_write(ctx->handle, offset, byte_width: width, value: data->value); |
353 | } |
354 | |
355 | static uacpi_status handle_io_region(uacpi_region_op op, uacpi_handle op_data) |
356 | { |
357 | switch (op) { |
358 | case UACPI_REGION_OP_ATTACH: |
359 | return io_region_attach(data: op_data); |
360 | case UACPI_REGION_OP_DETACH: |
361 | return io_region_detach(data: op_data); |
362 | case UACPI_REGION_OP_READ: |
363 | case UACPI_REGION_OP_WRITE: |
364 | return io_region_do_rw(op, data: op_data); |
365 | default: |
366 | return UACPI_STATUS_INVALID_ARGUMENT; |
367 | } |
368 | } |
369 | |
370 | void uacpi_install_default_address_space_handlers(void) |
371 | { |
372 | uacpi_namespace_node *root; |
373 | |
374 | root = uacpi_namespace_root(); |
375 | |
376 | uacpi_install_address_space_handler( |
377 | device_node: root, space: UACPI_ADDRESS_SPACE_SYSTEM_MEMORY, |
378 | handler: handle_memory_region, UACPI_NULL |
379 | ); |
380 | |
381 | uacpi_install_address_space_handler( |
382 | device_node: root, space: UACPI_ADDRESS_SPACE_SYSTEM_IO, |
383 | handler: handle_io_region, UACPI_NULL |
384 | ); |
385 | |
386 | uacpi_install_address_space_handler( |
387 | device_node: root, space: UACPI_ADDRESS_SPACE_PCI_CONFIG, |
388 | handler: handle_pci_region, UACPI_NULL |
389 | ); |
390 | |
391 | uacpi_install_address_space_handler( |
392 | device_node: root, space: UACPI_ADDRESS_SPACE_TABLE_DATA, |
393 | handler: handle_table_data_region, UACPI_NULL |
394 | ); |
395 | } |
396 | |