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