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
12static 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
40struct pci_region_ctx {
41 uacpi_pci_address address;
42};
43
44static 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
119static 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
127static 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
143static 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
158struct memory_region_ctx {
159 uacpi_phys_addr phys;
160 uacpi_u8 *virt;
161 uacpi_size size;
162};
163
164static 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
192static 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
201struct io_region_ctx {
202 uacpi_io_addr base;
203 uacpi_handle handle;
204};
205
206static 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
232static 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
241static 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
263static 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
285static 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
299static 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
314static 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
325static 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
339static 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
355static 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
370void 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