1 /*
2 * The PCI Library -- Darwin kIOACPI access
3 *
4 * Copyright (c) 2013 Apple, Inc.
5 *
6 * Can be freely distributed and used under the terms of the GNU GPL v2+.
7 *
8 * SPDX-License-Identifier: GPL-2.0-or-later
9 */
10
11 #include <errno.h>
12 #include <fcntl.h>
13 #include <stdio.h>
14 #include <string.h>
15 #include <unistd.h>
16 #include <stdint.h>
17
18 #include "internal.h"
19
20 #include <mach/mach_error.h>
21 #include <CoreFoundation/CoreFoundation.h>
22 #include <IOKit/IOKitLib.h>
23 #include <IOKit/IOKitKeys.h>
24
25 enum {
26 kACPIMethodAddressSpaceRead = 0,
27 kACPIMethodAddressSpaceWrite = 1,
28 kACPIMethodDebuggerCommand = 2,
29 kACPIMethodCount
30 };
31
32 #pragma pack(1)
33
34 typedef UInt32 IOACPIAddressSpaceID;
35
36 enum {
37 kIOACPIAddressSpaceIDSystemMemory = 0,
38 kIOACPIAddressSpaceIDSystemIO = 1,
39 kIOACPIAddressSpaceIDPCIConfiguration = 2,
40 kIOACPIAddressSpaceIDEmbeddedController = 3,
41 kIOACPIAddressSpaceIDSMBus = 4
42 };
43
44 /*
45 * 64-bit ACPI address
46 */
47 union IOACPIAddress {
48 UInt64 addr64;
49 struct {
50 unsigned int offset :16;
51 unsigned int function :3;
52 unsigned int device :5;
53 unsigned int bus :8;
54 unsigned int segment :16;
55 unsigned int reserved :16;
56 } pci;
57 };
58 typedef union IOACPIAddress IOACPIAddress;
59
60 #pragma pack()
61
62 struct AddressSpaceParam {
63 UInt64 value;
64 UInt32 spaceID;
65 IOACPIAddress address;
66 UInt32 bitWidth;
67 UInt32 bitOffset;
68 UInt32 options;
69 };
70 typedef struct AddressSpaceParam AddressSpaceParam;
71
72 static void
darwin_config(struct pci_access * a UNUSED)73 darwin_config(struct pci_access *a UNUSED)
74 {
75 }
76
77 static int
darwin_detect(struct pci_access * a)78 darwin_detect(struct pci_access *a)
79 {
80 io_registry_entry_t service;
81 io_connect_t connect;
82 kern_return_t status;
83
84 service = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("AppleACPIPlatformExpert"));
85 if (service)
86 {
87 status = IOServiceOpen(service, mach_task_self(), 0, &connect);
88 IOObjectRelease(service);
89 }
90
91 if (!service || (kIOReturnSuccess != status))
92 {
93 a->warning("Cannot open AppleACPIPlatformExpert (add boot arg debug=0x144 & run as root)");
94 return 0;
95 }
96 a->debug("...using AppleACPIPlatformExpert");
97 a->fd = connect;
98 return 1;
99 }
100
101 static void
darwin_init(struct pci_access * a UNUSED)102 darwin_init(struct pci_access *a UNUSED)
103 {
104 }
105
106 static void
darwin_cleanup(struct pci_access * a UNUSED)107 darwin_cleanup(struct pci_access *a UNUSED)
108 {
109 }
110
111 static int
darwin_read(struct pci_dev * d,int pos,byte * buf,int len)112 darwin_read(struct pci_dev *d, int pos, byte *buf, int len)
113 {
114 if (!(len == 1 || len == 2 || len == 4))
115 return pci_generic_block_read(d, pos, buf, len);
116
117 AddressSpaceParam param;
118 kern_return_t status;
119
120 param.spaceID = kIOACPIAddressSpaceIDPCIConfiguration;
121 param.bitWidth = len * 8;
122 param.bitOffset = 0;
123 param.options = 0;
124
125 param.address.pci.offset = pos;
126 param.address.pci.function = d->func;
127 param.address.pci.device = d->dev;
128 param.address.pci.bus = d->bus;
129 param.address.pci.segment = d->domain;
130 param.address.pci.reserved = 0;
131 param.value = -1ULL;
132
133 size_t outSize = sizeof(param);
134 status = IOConnectCallStructMethod(d->access->fd, kACPIMethodAddressSpaceRead,
135 ¶m, sizeof(param),
136 ¶m, &outSize);
137 if ((kIOReturnSuccess != status))
138 d->access->error("darwin_read: kACPIMethodAddressSpaceRead failed: %s", mach_error_string(status));
139
140 switch (len)
141 {
142 case 1:
143 buf[0] = (u8) param.value;
144 break;
145 case 2:
146 ((u16 *) buf)[0] = cpu_to_le16((u16) param.value);
147 break;
148 case 4:
149 ((u32 *) buf)[0] = cpu_to_le32((u32) param.value);
150 break;
151 }
152 return 1;
153 }
154
155 static int
darwin_write(struct pci_dev * d,int pos,byte * buf,int len)156 darwin_write(struct pci_dev *d, int pos, byte *buf, int len)
157 {
158 if (!(len == 1 || len == 2 || len == 4))
159 return pci_generic_block_write(d, pos, buf, len);
160
161 AddressSpaceParam param;
162 kern_return_t status;
163
164 param.spaceID = kIOACPIAddressSpaceIDPCIConfiguration;
165 param.bitWidth = len * 8;
166 param.bitOffset = 0;
167 param.options = 0;
168
169 param.address.pci.offset = pos;
170 param.address.pci.function = d->func;
171 param.address.pci.device = d->dev;
172 param.address.pci.bus = d->bus;
173 param.address.pci.segment = d->domain;
174 param.address.pci.reserved = 0;
175
176 switch (len)
177 {
178 case 1:
179 param.value = buf[0];
180 break;
181 case 2:
182 param.value = le16_to_cpu(((u16 *) buf)[0]);
183 break;
184 case 4:
185 param.value = le32_to_cpu(((u32 *) buf)[0]);
186 break;
187 }
188
189 size_t outSize = 0;
190 status = IOConnectCallStructMethod(d->access->fd, kACPIMethodAddressSpaceWrite,
191 ¶m, sizeof(param),
192 NULL, &outSize);
193 if ((kIOReturnSuccess != status))
194 d->access->error("darwin_read: kACPIMethodAddressSpaceWrite failed: %s", mach_error_string(status));
195
196 return 1;
197 }
198
199 struct pci_methods pm_darwin = {
200 .name = "darwin",
201 .help = "Darwin",
202 .config = darwin_config,
203 .detect = darwin_detect,
204 .init = darwin_init,
205 .cleanup = darwin_cleanup,
206 .scan = pci_generic_scan,
207 .fill_info = pci_generic_fill_info,
208 .read = darwin_read,
209 .write = darwin_write,
210 };
211