Blame view

drivers/acpi/processor_core.c 9.09 KB
457c89965   Thomas Gleixner   treewide: Add SPD...
1
  // SPDX-License-Identifier: GPL-2.0-only
47817254b   Alex Chiang   ACPI: processor: ...
2
3
4
5
6
7
  /*
   * Copyright (C) 2005 Intel Corporation
   * Copyright (C) 2009 Hewlett-Packard Development Company, L.P.
   *
   *	Alex Chiang <achiang@hp.com>
   *	- Unified x86/ia64 implementations
ecf5636dc   Yinghai Lu   ACPI: Add interfa...
8
9
10
11
   *
   * I/O APIC hotplug support
   *	Yinghai Lu <yinghai@kernel.org>
   *	Jiang Liu <jiang.liu@intel.com>
47817254b   Alex Chiang   ACPI: processor: ...
12
   */
214f2c90b   Paul Gortmaker   acpi: add export....
13
  #include <linux/export.h>
8b48463f8   Lv Zheng   ACPI: Clean up in...
14
  #include <linux/acpi.h>
78f169965   Alex Chiang   ACPI: processor: ...
15
  #include <acpi/processor.h>
ecf5636dc   Yinghai Lu   ACPI: Add interfa...
16
17
18
19
20
21
22
23
24
25
26
27
28
29
  static struct acpi_table_madt *get_madt_table(void)
  {
  	static struct acpi_table_madt *madt;
  	static int read_madt;
  
  	if (!read_madt) {
  		if (ACPI_FAILURE(acpi_get_table(ACPI_SIG_MADT, 0,
  					(struct acpi_table_header **)&madt)))
  			madt = NULL;
  		read_madt++;
  	}
  
  	return madt;
  }
78ed8bd29   Alex Chiang   ACPI: processor: ...
30
  static int map_lapic_id(struct acpi_subtable_header *entry,
09c3f2bd5   Dou Liyang   Revert"x86/acpi: ...
31
  		 u32 acpi_id, phys_cpuid_t *apic_id)
78ed8bd29   Alex Chiang   ACPI: processor: ...
32
33
  {
  	struct acpi_madt_local_apic *lapic =
ef86c3f4b   Fabian Frederick   ACPI / processor:...
34
  		container_of(entry, struct acpi_madt_local_apic, header);
11130736c   Alex Chiang   ACPI: processor: ...
35

09c3f2bd5   Dou Liyang   Revert"x86/acpi: ...
36
  	if (!(lapic->lapic_flags & ACPI_MADT_ENABLED))
038d7b593   Hanjun Guo   ACPI / processor:...
37
  		return -ENODEV;
11130736c   Alex Chiang   ACPI: processor: ...
38
39
  
  	if (lapic->processor_id != acpi_id)
038d7b593   Hanjun Guo   ACPI / processor:...
40
  		return -EINVAL;
11130736c   Alex Chiang   ACPI: processor: ...
41
42
  
  	*apic_id = lapic->id;
038d7b593   Hanjun Guo   ACPI / processor:...
43
  	return 0;
78ed8bd29   Alex Chiang   ACPI: processor: ...
44
45
46
  }
  
  static int map_x2apic_id(struct acpi_subtable_header *entry,
09c3f2bd5   Dou Liyang   Revert"x86/acpi: ...
47
  		int device_declaration, u32 acpi_id, phys_cpuid_t *apic_id)
78ed8bd29   Alex Chiang   ACPI: processor: ...
48
49
  {
  	struct acpi_madt_local_x2apic *apic =
ef86c3f4b   Fabian Frederick   ACPI / processor:...
50
  		container_of(entry, struct acpi_madt_local_x2apic, header);
78ed8bd29   Alex Chiang   ACPI: processor: ...
51

09c3f2bd5   Dou Liyang   Revert"x86/acpi: ...
52
  	if (!(apic->lapic_flags & ACPI_MADT_ENABLED))
038d7b593   Hanjun Guo   ACPI / processor:...
53
  		return -ENODEV;
78ed8bd29   Alex Chiang   ACPI: processor: ...
54

d67420956   Alex Chiang   ACPI: processor: ...
55
56
  	if (device_declaration && (apic->uid == acpi_id)) {
  		*apic_id = apic->local_apic_id;
038d7b593   Hanjun Guo   ACPI / processor:...
57
  		return 0;
78ed8bd29   Alex Chiang   ACPI: processor: ...
58
  	}
038d7b593   Hanjun Guo   ACPI / processor:...
59
  	return -EINVAL;
78ed8bd29   Alex Chiang   ACPI: processor: ...
60
61
62
  }
  
  static int map_lsapic_id(struct acpi_subtable_header *entry,
09c3f2bd5   Dou Liyang   Revert"x86/acpi: ...
63
  		int device_declaration, u32 acpi_id, phys_cpuid_t *apic_id)
78ed8bd29   Alex Chiang   ACPI: processor: ...
64
65
  {
  	struct acpi_madt_local_sapic *lsapic =
ef86c3f4b   Fabian Frederick   ACPI / processor:...
66
  		container_of(entry, struct acpi_madt_local_sapic, header);
78ed8bd29   Alex Chiang   ACPI: processor: ...
67

09c3f2bd5   Dou Liyang   Revert"x86/acpi: ...
68
  	if (!(lsapic->lapic_flags & ACPI_MADT_ENABLED))
038d7b593   Hanjun Guo   ACPI / processor:...
69
  		return -ENODEV;
78ed8bd29   Alex Chiang   ACPI: processor: ...
70

78ed8bd29   Alex Chiang   ACPI: processor: ...
71
  	if (device_declaration) {
eae701cea   Alex Chiang   ACPI: processor: ...
72
  		if ((entry->length < 16) || (lsapic->uid != acpi_id))
038d7b593   Hanjun Guo   ACPI / processor:...
73
  			return -EINVAL;
eae701cea   Alex Chiang   ACPI: processor: ...
74
  	} else if (lsapic->processor_id != acpi_id)
038d7b593   Hanjun Guo   ACPI / processor:...
75
  		return -EINVAL;
78ed8bd29   Alex Chiang   ACPI: processor: ...
76

eae701cea   Alex Chiang   ACPI: processor: ...
77
  	*apic_id = (lsapic->id << 8) | lsapic->eid;
038d7b593   Hanjun Guo   ACPI / processor:...
78
  	return 0;
78ed8bd29   Alex Chiang   ACPI: processor: ...
79
  }
020295b4c   Hanjun Guo   ACPI / processor:...
80
81
82
83
  /*
   * Retrieve the ARM CPU physical identifier (MPIDR)
   */
  static int map_gicc_mpidr(struct acpi_subtable_header *entry,
09c3f2bd5   Dou Liyang   Revert"x86/acpi: ...
84
  		int device_declaration, u32 acpi_id, phys_cpuid_t *mpidr)
020295b4c   Hanjun Guo   ACPI / processor:...
85
86
87
  {
  	struct acpi_madt_generic_interrupt *gicc =
  	    container_of(entry, struct acpi_madt_generic_interrupt, header);
09c3f2bd5   Dou Liyang   Revert"x86/acpi: ...
88
  	if (!(gicc->flags & ACPI_MADT_ENABLED))
020295b4c   Hanjun Guo   ACPI / processor:...
89
90
91
92
93
94
95
96
97
98
99
100
101
102
  		return -ENODEV;
  
  	/* device_declaration means Device object in DSDT, in the
  	 * GIC interrupt model, logical processors are required to
  	 * have a Processor Device object in the DSDT, so we should
  	 * check device_declaration here
  	 */
  	if (device_declaration && (gicc->uid == acpi_id)) {
  		*mpidr = gicc->arm_mpidr;
  		return 0;
  	}
  
  	return -EINVAL;
  }
fb7c2bae8   David Daney   ACPI / processor:...
103
  static phys_cpuid_t map_madt_entry(struct acpi_table_madt *madt,
09c3f2bd5   Dou Liyang   Revert"x86/acpi: ...
104
  				   int type, u32 acpi_id)
78ed8bd29   Alex Chiang   ACPI: processor: ...
105
106
  {
  	unsigned long madt_end, entry;
828aef376   Catalin Marinas   ACPI / processor:...
107
  	phys_cpuid_t phys_id = PHYS_CPUID_INVALID;	/* CPU hardware ID */
78ed8bd29   Alex Chiang   ACPI: processor: ...
108
109
  
  	if (!madt)
af8f3f514   Hanjun Guo   ACPI / processor:...
110
  		return phys_id;
78ed8bd29   Alex Chiang   ACPI: processor: ...
111
112
113
114
115
116
117
118
119
120
121
  
  	entry = (unsigned long)madt;
  	madt_end = entry + madt->header.length;
  
  	/* Parse all entries looking for a match. */
  
  	entry += sizeof(struct acpi_table_madt);
  	while (entry + sizeof(struct acpi_subtable_header) < madt_end) {
  		struct acpi_subtable_header *header =
  			(struct acpi_subtable_header *)entry;
  		if (header->type == ACPI_MADT_TYPE_LOCAL_APIC) {
09c3f2bd5   Dou Liyang   Revert"x86/acpi: ...
122
  			if (!map_lapic_id(header, acpi_id, &phys_id))
78ed8bd29   Alex Chiang   ACPI: processor: ...
123
124
  				break;
  		} else if (header->type == ACPI_MADT_TYPE_LOCAL_X2APIC) {
09c3f2bd5   Dou Liyang   Revert"x86/acpi: ...
125
  			if (!map_x2apic_id(header, type, acpi_id, &phys_id))
78ed8bd29   Alex Chiang   ACPI: processor: ...
126
127
  				break;
  		} else if (header->type == ACPI_MADT_TYPE_LOCAL_SAPIC) {
09c3f2bd5   Dou Liyang   Revert"x86/acpi: ...
128
  			if (!map_lsapic_id(header, type, acpi_id, &phys_id))
78ed8bd29   Alex Chiang   ACPI: processor: ...
129
  				break;
020295b4c   Hanjun Guo   ACPI / processor:...
130
  		} else if (header->type == ACPI_MADT_TYPE_GENERIC_INTERRUPT) {
09c3f2bd5   Dou Liyang   Revert"x86/acpi: ...
131
  			if (!map_gicc_mpidr(header, type, acpi_id, &phys_id))
020295b4c   Hanjun Guo   ACPI / processor:...
132
  				break;
78ed8bd29   Alex Chiang   ACPI: processor: ...
133
134
135
  		}
  		entry += header->length;
  	}
af8f3f514   Hanjun Guo   ACPI / processor:...
136
  	return phys_id;
78ed8bd29   Alex Chiang   ACPI: processor: ...
137
  }
fb7c2bae8   David Daney   ACPI / processor:...
138
139
140
  phys_cpuid_t __init acpi_map_madt_entry(u32 acpi_id)
  {
  	struct acpi_table_madt *madt = NULL;
fb7c2bae8   David Daney   ACPI / processor:...
141
  	phys_cpuid_t rv;
6b11d1d67   Lv Zheng   ACPI / osl: Remov...
142
143
  	acpi_get_table(ACPI_SIG_MADT, 0,
  		       (struct acpi_table_header **)&madt);
fb7c2bae8   David Daney   ACPI / processor:...
144
145
  	if (!madt)
  		return PHYS_CPUID_INVALID;
09c3f2bd5   Dou Liyang   Revert"x86/acpi: ...
146
  	rv = map_madt_entry(madt, 1, acpi_id);
fb7c2bae8   David Daney   ACPI / processor:...
147

6b11d1d67   Lv Zheng   ACPI / osl: Remov...
148
  	acpi_put_table((struct acpi_table_header *)madt);
fb7c2bae8   David Daney   ACPI / processor:...
149
150
151
  
  	return rv;
  }
09c3f2bd5   Dou Liyang   Revert"x86/acpi: ...
152
  static phys_cpuid_t map_mat_entry(acpi_handle handle, int type, u32 acpi_id)
78ed8bd29   Alex Chiang   ACPI: processor: ...
153
154
155
156
  {
  	struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
  	union acpi_object *obj;
  	struct acpi_subtable_header *header;
828aef376   Catalin Marinas   ACPI / processor:...
157
  	phys_cpuid_t phys_id = PHYS_CPUID_INVALID;
78ed8bd29   Alex Chiang   ACPI: processor: ...
158
159
160
161
162
163
164
165
166
167
168
169
170
171
  
  	if (ACPI_FAILURE(acpi_evaluate_object(handle, "_MAT", NULL, &buffer)))
  		goto exit;
  
  	if (!buffer.length || !buffer.pointer)
  		goto exit;
  
  	obj = buffer.pointer;
  	if (obj->type != ACPI_TYPE_BUFFER ||
  	    obj->buffer.length < sizeof(struct acpi_subtable_header)) {
  		goto exit;
  	}
  
  	header = (struct acpi_subtable_header *)obj->buffer.pointer;
13ca62b24   Jiang Liu   ACPI: Fix minor s...
172
  	if (header->type == ACPI_MADT_TYPE_LOCAL_APIC)
09c3f2bd5   Dou Liyang   Revert"x86/acpi: ...
173
  		map_lapic_id(header, acpi_id, &phys_id);
13ca62b24   Jiang Liu   ACPI: Fix minor s...
174
  	else if (header->type == ACPI_MADT_TYPE_LOCAL_SAPIC)
09c3f2bd5   Dou Liyang   Revert"x86/acpi: ...
175
  		map_lsapic_id(header, type, acpi_id, &phys_id);
13ca62b24   Jiang Liu   ACPI: Fix minor s...
176
  	else if (header->type == ACPI_MADT_TYPE_LOCAL_X2APIC)
09c3f2bd5   Dou Liyang   Revert"x86/acpi: ...
177
  		map_x2apic_id(header, type, acpi_id, &phys_id);
020295b4c   Hanjun Guo   ACPI / processor:...
178
  	else if (header->type == ACPI_MADT_TYPE_GENERIC_INTERRUPT)
09c3f2bd5   Dou Liyang   Revert"x86/acpi: ...
179
  		map_gicc_mpidr(header, type, acpi_id, &phys_id);
78ed8bd29   Alex Chiang   ACPI: processor: ...
180
181
  
  exit:
5273a2583   Syam Sidhardhan   ACPI / processor:...
182
  	kfree(buffer.pointer);
af8f3f514   Hanjun Guo   ACPI / processor:...
183
  	return phys_id;
78ed8bd29   Alex Chiang   ACPI: processor: ...
184
  }
09c3f2bd5   Dou Liyang   Revert"x86/acpi: ...
185
  phys_cpuid_t acpi_get_phys_id(acpi_handle handle, int type, u32 acpi_id)
78ed8bd29   Alex Chiang   ACPI: processor: ...
186
  {
828aef376   Catalin Marinas   ACPI / processor:...
187
  	phys_cpuid_t phys_id;
78ed8bd29   Alex Chiang   ACPI: processor: ...
188

09c3f2bd5   Dou Liyang   Revert"x86/acpi: ...
189
  	phys_id = map_mat_entry(handle, type, acpi_id);
ddcc18f5b   Hanjun Guo   ACPI / processor:...
190
  	if (invalid_phys_cpuid(phys_id))
09c3f2bd5   Dou Liyang   Revert"x86/acpi: ...
191
  		phys_id = map_madt_entry(get_madt_table(), type, acpi_id);
ca9f62ac7   Jiang Liu   ACPI / processor:...
192

af8f3f514   Hanjun Guo   ACPI / processor:...
193
  	return phys_id;
ca9f62ac7   Jiang Liu   ACPI / processor:...
194
  }
166deb0f0   Jan Beulich   xen/ACPI: don't u...
195
  EXPORT_SYMBOL_GPL(acpi_get_phys_id);
ca9f62ac7   Jiang Liu   ACPI / processor:...
196

828aef376   Catalin Marinas   ACPI / processor:...
197
  int acpi_map_cpuid(phys_cpuid_t phys_id, u32 acpi_id)
ca9f62ac7   Jiang Liu   ACPI / processor:...
198
199
200
201
  {
  #ifdef CONFIG_SMP
  	int i;
  #endif
ddcc18f5b   Hanjun Guo   ACPI / processor:...
202
  	if (invalid_phys_cpuid(phys_id)) {
d640113fe   Lin Ming   ACPI: processor: ...
203
204
  		/*
  		 * On UP processor, there is no _MAT or MADT table.
828aef376   Catalin Marinas   ACPI / processor:...
205
  		 * So above phys_id is always set to PHYS_CPUID_INVALID.
d640113fe   Lin Ming   ACPI: processor: ...
206
207
208
209
210
  		 *
  		 * BIOS may define multiple CPU handles even for UP processor.
  		 * For example,
  		 *
  		 * Scope (_PR)
13ca62b24   Jiang Liu   ACPI: Fix minor s...
211
  		 * {
d640113fe   Lin Ming   ACPI: processor: ...
212
213
214
215
216
217
  		 *     Processor (CPU0, 0x00, 0x00000410, 0x06) {}
  		 *     Processor (CPU1, 0x01, 0x00000410, 0x06) {}
  		 *     Processor (CPU2, 0x02, 0x00000410, 0x06) {}
  		 *     Processor (CPU3, 0x03, 0x00000410, 0x06) {}
  		 * }
  		 *
af8f3f514   Hanjun Guo   ACPI / processor:...
218
  		 * Ignores phys_id and always returns 0 for the processor
c4686c71a   Thomas Renninger   cpufreq / ACPI: F...
219
220
  		 * handle with acpi id 0 if nr_cpu_ids is 1.
  		 * This should be the case if SMP tables are not found.
d3da7cb9d   Hanjun Guo   ACPI / processor:...
221
  		 * Return -EINVAL for other CPU's handle.
d640113fe   Lin Ming   ACPI: processor: ...
222
  		 */
c4686c71a   Thomas Renninger   cpufreq / ACPI: F...
223
  		if (nr_cpu_ids <= 1 && acpi_id == 0)
d640113fe   Lin Ming   ACPI: processor: ...
224
225
  			return acpi_id;
  		else
d3da7cb9d   Hanjun Guo   ACPI / processor:...
226
  			return -EINVAL;
d640113fe   Lin Ming   ACPI: processor: ...
227
  	}
78ed8bd29   Alex Chiang   ACPI: processor: ...
228

932df7414   Lin Ming   ACPI: processor: ...
229
  #ifdef CONFIG_SMP
78ed8bd29   Alex Chiang   ACPI: processor: ...
230
  	for_each_possible_cpu(i) {
af8f3f514   Hanjun Guo   ACPI / processor:...
231
  		if (cpu_physical_id(i) == phys_id)
78ed8bd29   Alex Chiang   ACPI: processor: ...
232
233
  			return i;
  	}
932df7414   Lin Ming   ACPI: processor: ...
234
235
  #else
  	/* In UP kernel, only processor 0 is valid */
af8f3f514   Hanjun Guo   ACPI / processor:...
236
237
  	if (phys_id == 0)
  		return phys_id;
932df7414   Lin Ming   ACPI: processor: ...
238
  #endif
d3da7cb9d   Hanjun Guo   ACPI / processor:...
239
  	return -ENODEV;
78ed8bd29   Alex Chiang   ACPI: processor: ...
240
  }
ca9f62ac7   Jiang Liu   ACPI / processor:...
241
242
243
  
  int acpi_get_cpuid(acpi_handle handle, int type, u32 acpi_id)
  {
828aef376   Catalin Marinas   ACPI / processor:...
244
  	phys_cpuid_t phys_id;
ca9f62ac7   Jiang Liu   ACPI / processor:...
245

af8f3f514   Hanjun Guo   ACPI / processor:...
246
  	phys_id = acpi_get_phys_id(handle, type, acpi_id);
ca9f62ac7   Jiang Liu   ACPI / processor:...
247

af8f3f514   Hanjun Guo   ACPI / processor:...
248
  	return acpi_map_cpuid(phys_id, acpi_id);
ca9f62ac7   Jiang Liu   ACPI / processor:...
249
  }
78ed8bd29   Alex Chiang   ACPI: processor: ...
250
  EXPORT_SYMBOL_GPL(acpi_get_cpuid);
ecf5636dc   Yinghai Lu   ACPI: Add interfa...
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
  
  #ifdef CONFIG_ACPI_HOTPLUG_IOAPIC
  static int get_ioapic_id(struct acpi_subtable_header *entry, u32 gsi_base,
  			 u64 *phys_addr, int *ioapic_id)
  {
  	struct acpi_madt_io_apic *ioapic = (struct acpi_madt_io_apic *)entry;
  
  	if (ioapic->global_irq_base != gsi_base)
  		return 0;
  
  	*phys_addr = ioapic->address;
  	*ioapic_id = ioapic->id;
  	return 1;
  }
  
  static int parse_madt_ioapic_entry(u32 gsi_base, u64 *phys_addr)
  {
  	struct acpi_subtable_header *hdr;
  	unsigned long madt_end, entry;
  	struct acpi_table_madt *madt;
  	int apic_id = -1;
  
  	madt = get_madt_table();
  	if (!madt)
  		return apic_id;
  
  	entry = (unsigned long)madt;
  	madt_end = entry + madt->header.length;
  
  	/* Parse all entries looking for a match. */
  	entry += sizeof(struct acpi_table_madt);
  	while (entry + sizeof(struct acpi_subtable_header) < madt_end) {
  		hdr = (struct acpi_subtable_header *)entry;
  		if (hdr->type == ACPI_MADT_TYPE_IO_APIC &&
  		    get_ioapic_id(hdr, gsi_base, phys_addr, &apic_id))
  			break;
  		else
  			entry += hdr->length;
  	}
  
  	return apic_id;
  }
  
  static int parse_mat_ioapic_entry(acpi_handle handle, u32 gsi_base,
  				  u64 *phys_addr)
  {
  	struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
  	struct acpi_subtable_header *header;
  	union acpi_object *obj;
  	int apic_id = -1;
  
  	if (ACPI_FAILURE(acpi_evaluate_object(handle, "_MAT", NULL, &buffer)))
  		goto exit;
  
  	if (!buffer.length || !buffer.pointer)
  		goto exit;
  
  	obj = buffer.pointer;
  	if (obj->type != ACPI_TYPE_BUFFER ||
  	    obj->buffer.length < sizeof(struct acpi_subtable_header))
  		goto exit;
  
  	header = (struct acpi_subtable_header *)obj->buffer.pointer;
  	if (header->type == ACPI_MADT_TYPE_IO_APIC)
  		get_ioapic_id(header, gsi_base, phys_addr, &apic_id);
  
  exit:
  	kfree(buffer.pointer);
  	return apic_id;
  }
  
  /**
   * acpi_get_ioapic_id - Get IOAPIC ID and physical address matching @gsi_base
   * @handle:	ACPI object for IOAPIC device
   * @gsi_base:	GSI base to match with
   * @phys_addr:	Pointer to store physical address of matching IOAPIC record
   *
   * Walk resources returned by ACPI_MAT method, then ACPI MADT table, to search
   * for an ACPI IOAPIC record matching @gsi_base.
   * Return IOAPIC id and store physical address in @phys_addr if found a match,
   * otherwise return <0.
   */
  int acpi_get_ioapic_id(acpi_handle handle, u32 gsi_base, u64 *phys_addr)
  {
  	int apic_id;
  
  	apic_id = parse_mat_ioapic_entry(handle, gsi_base, phys_addr);
  	if (apic_id == -1)
  		apic_id = parse_madt_ioapic_entry(gsi_base, phys_addr);
  
  	return apic_id;
  }
  #endif /* CONFIG_ACPI_HOTPLUG_IOAPIC */