summaryrefslogtreecommitdiffstats
path: root/include/afb/afb-req-itf.h
blob: 8c18a83177e6ed75040d26772fa3bf76070b3a91 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
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
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
/*
 * Copyright (C) 2016, 2017 "IoT.bzh"
 * Author: José Bollo <jose.bollo@iot.bzh>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#pragma once

#include <stdarg.h>

#include "afb-event-itf.h"

/* avoid inclusion of <json-c/json.h> */
struct json_object;

/*
 * Describes an argument (or parameter) of a request
 */
struct afb_arg {
	const char *name;	/* name of the argument or NULL if invalid */
	const char *value;	/* string representation of the value of the argument */
				/* original filename of the argument if path != NULL */
	const char *path;	/* if not NULL, path of the received file for the argument */
				/* when the request is finalized this file is removed */
};

/*
 * Interface for handling requests.
 * It records the functions to be called for the request.
 * Don't use this structure directly.
 * Use the helper functions documented below.
 */
struct afb_req_itf {
	/* CAUTION: respect the order, add at the end */

	struct json_object *(*json)(void *closure);
	struct afb_arg (*get)(void *closure, const char *name);

	void (*success)(void *closure, struct json_object *obj, const char *info);
	void (*fail)(void *closure, const char *status, const char *info);

	void (*vsuccess)(void *closure, struct json_object *obj, const char *fmt, va_list args);
	void (*vfail)(void *closure, const char *status, const char *fmt, va_list args);

	void *(*context_get)(void *closure);
	void (*context_set)(void *closure, void *value, void (*free_value)(void*));

	void (*addref)(void *closure);
	void (*unref)(void *closure);

	void (*session_close)(void *closure);
	int (*session_set_LOA)(void *closure, unsigned level);

	int (*subscribe)(void *closure, struct afb_event event);
	int (*unsubscribe)(void *closure, struct afb_event event);

	void (*subcall)(void *closure, const char *api, const char *verb, struct json_object *args, void (*callback)(void*, int, struct json_object*), void *cb_closure);
	int (*subcallsync)(void *closure, const char *api, const char *verb, struct json_object *args, struct json_object **result);

	void (*vverbose)(void*closure, int level, const char *file, int line, const char * func, const char *fmt, va_list args);
};

/*
 * Describes the request by bindings from afb-daemon
 */
struct afb_req {
	const struct afb_req_itf *itf;	/* the interface to use */
	void *closure;			/* the closure argument for functions of 'itf' */
};

/*
 * Checks wether the request 'req' is valid or not.
 *
 * Returns 0 if not valid or 1 if valid.
 */
static inline int afb_req_is_valid(struct afb_req req)
{
	return req.itf != NULL;
}

/*
 * Gets from the request 'req' the argument of 'name'.
 * Returns a PLAIN structure of type 'struct afb_arg'.
 * When the argument of 'name' is not found, all fields of result are set to NULL.
 * When the argument of 'name' is found, the fields are filled,
 * in particular, the field 'result.name' is set to 'name'.
 *
 * There is a special name value: the empty string.
 * The argument of name "" is defined only if the request was made using
 * an HTTP POST of Content-Type "application/json". In that case, the
 * argument of name "" receives the value of the body of the HTTP request.
 */
static inline struct afb_arg afb_req_get(struct afb_req req, const char *name)
{
	return req.itf->get(req.closure, name);
}

/*
 * Gets from the request 'req' the string value of the argument of 'name'.
 * Returns NULL if when there is no argument of 'name'.
 * Returns the value of the argument of 'name' otherwise.
 *
 * Shortcut for: afb_req_get(req, name).value
 */
static inline const char *afb_req_value(struct afb_req req, const char *name)
{
	return afb_req_get(req, name).value;
}

/*
 * Gets from the request 'req' the path for file attached to the argument of 'name'.
 * Returns NULL if when there is no argument of 'name' or when there is no file.
 * Returns the path of the argument of 'name' otherwise.
 *
 * Shortcut for: afb_req_get(req, name).path
 */
static inline const char *afb_req_path(struct afb_req req, const char *name)
{
	return afb_req_get(req, name).path;
}

/*
 * Gets from the request 'req' the json object hashing the arguments.
 * The returned object must not be released using 'json_object_put'.
 */
static inline struct json_object *afb_req_json(struct afb_req req)
{
	return req.itf->json(req.closure);
}

/*
 * Sends a reply of kind success to the request 'req'.
 * The status of the reply is automatically set to "success".
 * Its send the object 'obj' (can be NULL) with an
 * informationnal comment 'info (can also be NULL).
 *
 * For convenience, the function calls 'json_object_put' for 'obj'.
 * Thus, in the case where 'obj' should remain available after
 * the function returns, the function 'json_object_get' shall be used.
 */
static inline void afb_req_success(struct afb_req req, struct json_object *obj, const char *info)
{
	req.itf->success(req.closure, obj, info);
}

/*
 * Same as 'afb_req_success' but the 'info' is a formatting
 * string followed by arguments.
 *
 * For convenience, the function calls 'json_object_put' for 'obj'.
 * Thus, in the case where 'obj' should remain available after
 * the function returns, the function 'json_object_get' shall be used.
 */
static inline void afb_req_success_f(struct afb_req req, struct json_object *obj, const char *info, ...) __attribute__((format(printf, 3, 4)));
static inline void afb_req_success_f(struct afb_req req, struct json_object *obj, const char *info, ...)
{
	va_list args;
	va_start(args, info);
	req.itf->vsuccess(req.closure, obj, info, args);
	va_end(args);
}

/*
 * Same as 'afb_req_success_f' but the arguments to the format 'info'
 * are given as a variable argument list instance.
 *
 * For convenience, the function calls 'json_object_put' for 'obj'.
 * Thus, in the case where 'obj' should remain available after
 * the function returns, the function 'json_object_get' shall be used.
 */
static inline void afb_req_success_v(struct afb_req req, struct json_object *obj, const char *info, va_list args)
{
	req.itf->vsuccess(req.closure, obj, info, args);
}

/*
 * Sends a reply of kind failure to the request 'req'.
 * The status of the reply is set to 'status' and an
 * informationnal comment 'info' (can also be NULL) can be added.
 *
 * Note that calling afb_req_fail("success", info) is equivalent
 * to call afb_req_success(NULL, info). Thus even if possible it
 * is strongly recommanded to NEVER use "success" for status.
 */
static inline void afb_req_fail(struct afb_req req, const char *status, const char *info)
{
	req.itf->fail(req.closure, status, info);
}

/*
 * Same as 'afb_req_fail' but the 'info' is a formatting
 * string followed by arguments.
 */
static inline void afb_req_fail_f(struct afb_req req, const char *status, const char *info, ...) __attribute__((format(printf, 3, 4)));
static inline void afb_req_fail_f(struct afb_req req, const char *status, const char *info, ...)
{
	va_list args;
	va_start(args, info);
	req.itf->vfail(req.closure, status, info, args);
	va_end(args);
}

/*
 * Same as 'afb_req_fail_f' but the arguments to the format 'info'
 * are given as a variable argument list instance.
 */
static inline void afb_req_fail_v(struct afb_req req, const char *status, const char *info, va_list args)
{
	req.itf->vfail(req.closure, status, info, args);
}

/*
 * Gets the pointer stored by the binding for the session of 'req'.
 * When the binding has not yet recorded a pointer, NULL is returned.
 */
static inline void *afb_req_context_get(struct afb_req req)
{
	return req.itf->context_get(req.closure);
}

/*
 * Stores for the binding the pointer 'context' to the session of 'req'.
 * The function 'free_context' will be called when the session is closed
 * or if binding stores an other pointer.
 */
static inline void afb_req_context_set(struct afb_req req, void *context, void (*free_context)(void*))
{
	req.itf->context_set(req.closure, context, free_context);
}

/*
 * Gets the pointer stored by the binding for the session of 'req'.
 * If the stored pointer is NULL, indicating that no pointer was
 * already stored, afb_req_context creates a new context by calling
 * the function 'create_context' and stores it with the freeing function
 * 'free_context'.
 */
static inline void *afb_req_context(struct afb_req req, void *(*create_context)(), void (*free_context)(void*))
{
	void *result = afb_req_context_get(req);
	if (result == NULL) {
		result = create_context();
		afb_req_context_set(req, result, free_context);
	}
	return result;
}

/*
 * Frees the pointer stored by the binding for the session of 'req'
 * and sets it to NULL.
 *
 * Shortcut for: afb_req_context_set(req, NULL, NULL)
 */
static inline void afb_req_context_clear(struct afb_req req)
{
	afb_req_context_set(req, NULL, NULL);
}

/*
 * Adds one to the count of references of 'req'.
 * This function MUST be called by asynchronous implementations
 * of verbs if no reply was sent before returning.
 */
static inline void afb_req_addref(struct afb_req req)
{
	req.itf->addref(req.closure);
}

/*
 * Substracts one to the count of references of 'req'.
 * This function MUST be called by asynchronous implementations
 * of verbs after sending the asynchronous reply.
 */
static inline void afb_req_unref(struct afb_req req)
{
	req.itf->unref(req.closure);
}

/*
 * Closes the session associated with 'req'
 * and delete all associated contexts.
 */
static inline void afb_req_session_close(struct afb_req req)
{
	req.itf->session_close(req.closure);
}

/*
 * Sets the level of assurance of the session of 'req'
 * to 'level'. The effect of this function is subject of
 * security policies.
 * Returns 1 on success or 0 if failed.
 */
static inline int afb_req_session_set_LOA(struct afb_req req, unsigned level)
{
	return req.itf->session_set_LOA(req.closure, level);
}

/*
 * Stores 'req' on heap for asynchrnous use.
 * Returns a pointer to the stored 'req' or NULL on memory depletion.
 * The count of reference to 'req' is incremented on success
 * (see afb_req_addref).
 */
static inline struct afb_req *afb_req_store(struct afb_req req)
{
	struct afb_req *result = (struct afb_req*)malloc(sizeof *result);
	if (result != NULL) {
		*result = req;
		afb_req_addref(req);
	}
	return result;
}

/*
 * Retrieves the afb_req stored at 'req' and frees the memory.
 * Returns the stored request.
 * The count of reference is UNCHANGED, thus, normally, the
 * function 'afb_req_unref' should be called on the result
 * after that the asynchronous reply if sent.
 */
static inline struct afb_req afb_req_unstore(struct afb_req *req)
{
	struct afb_req result = *req;
	free(req);
	return result;
}

/*
 * Establishes for the client link identified by 'req' a subscription
 * to the 'event'.
 * Returns 0 in case of successful subscription or -1 in case of error.
 */
static inline int afb_req_subscribe(struct afb_req req, struct afb_event event)
{
	return req.itf->subscribe(req.closure, event);
}

/*
 * Revokes the subscription established to the 'event' for the client
 * link identified by 'req'.
 * Returns 0 in case of successful subscription or -1 in case of error.
 */
static inline int afb_req_unsubscribe(struct afb_req req, struct afb_event event)
{
	return req.itf->unsubscribe(req.closure, event);
}

/*
 * Makes a call to the method of name 'api' / 'verb' with the object 'args'.
 * This call is made in the context of the request 'req'.
 * On completion, the function 'callback' is invoked with the
 * 'closure' given at call and two other parameters: 'iserror' and 'result'.
 * 'iserror' is a boolean that indicates if the reply is an error reply.
 * 'result' is the json object of the reply, you must not call json_object_put
 * on the result.
 *
 * For convenience, the function calls 'json_object_put' for 'args'.
 * Thus, in the case where 'args' should remain available after
 * the function returns, the function 'json_object_get' shall be used.
 */
static inline void afb_req_subcall(struct afb_req req, const char *api, const char *verb, struct json_object *args, void (*callback)(void *closure, int iserror, struct json_object *result), void *closure)
{
	req.itf->subcall(req.closure, api, verb, args, callback, closure);
}

/*
 * Makes a call to the method of name 'api' / 'verb' with the object 'args'.
 * This call is made in the context of the request 'req'.
 * This call is synchronous, it waits untill completion of the request.
 * It returns 0 on an error answer and returns 1 when no error was detected.
 * The object pointed by 'result' is filled and must be released by the caller
 * after its use by calling 'json_object_put'.
 *
 * For convenience, the function calls 'json_object_put' for 'args'.
 * Thus, in the case where 'args' should remain available after
 * the function returns, the function 'json_object_get' shall be used.
 */
static inline int afb_req_subcall_sync(struct afb_req req, const char *api, const char *verb, struct json_object *args, struct json_object **result)
{
	return req.itf->subcallsync(req.closure, api, verb, args, result);
}

/*
 * Send associated to 'req' a message described by 'fmt' and following parameters
 * to the journal for the verbosity 'level'.
 *
 * 'file', 'line' and 'func' are indicators of position of the code in source files
 * (see macros __FILE__, __LINE__ and __func__).
 *
 * 'level' is defined by syslog standard:
 *      EMERGENCY         0        System is unusable
 *      ALERT             1        Action must be taken immediately
 *      CRITICAL          2        Critical conditions
 *      ERROR             3        Error conditions
 *      WARNING           4        Warning conditions
 *      NOTICE            5        Normal but significant condition
 *      INFO              6        Informational
 *      DEBUG             7        Debug-level messages
 */
static inline void afb_req_verbose(struct afb_req req, int level, const char *file, int line, const char * func, const char *fmt, ...) __attribute__((format(printf, 6, 7)));
static inline void afb_req_verbose(struct afb_req req, int level, const char *file, int line, const char * func, const char *fmt, ...)
{
	va_list args;
	va_start(args, fmt);
	req.itf->vverbose(req.closure, level, file, line, func, fmt, args);
	va_end(args);
}

/* macro for setting file, line and function automatically */
# if !defined(AFB_BINDING_PRAGMA_NO_VERBOSE_DETAILS)
#define AFB_REQ_VERBOSE(req,level,...) afb_req_verbose(req,level,__FILE__,__LINE__,__func__,__VA_ARGS__)
#else
#define AFB_REQ_VERBOSE(req,level,...) afb_req_verbose(req,level,NULL,0,NULL,__VA_ARGS__)
#endif
s="p">; UINTN Index; UINTN NumberOfDescriptors; EFI_GCD_IO_SPACE_DESCRIPTOR *IoSpaceMap; Status = gDS->GetIoSpaceMap (&NumberOfDescriptors, &IoSpaceMap); if (EFI_ERROR (Status)) { DEBUG ((EFI_D_ERROR, "%a: %a: GetIoSpaceMap(): %r\n", gEfiCallerBaseName, __FUNCTION__, Status)); return Status; } for (Index = 0; Index < NumberOfDescriptors; Index++) { Status = IntersectIoDescriptor (Base, Length, &IoSpaceMap[Index]); if (EFI_ERROR (Status)) { goto FreeIoSpaceMap; } } DEBUG_CODE ( // // Make sure there are adjacent descriptors covering [Base, Base + Length). // It is possible that they have not been merged; merging can be prevented // by allocation. // UINT64 CheckBase; EFI_STATUS CheckStatus; EFI_GCD_IO_SPACE_DESCRIPTOR Descriptor; for (CheckBase = Base; CheckBase < Base + Length; CheckBase = Descriptor.BaseAddress + Descriptor.Length) { CheckStatus = gDS->GetIoSpaceDescriptor (CheckBase, &Descriptor); ASSERT_EFI_ERROR (CheckStatus); ASSERT (Descriptor.GcdIoType == EfiGcdIoTypeIo); } ); FreeIoSpaceMap: FreePool (IoSpaceMap); return Status; } /** Ensure the compatibility of a memory space descriptor with the MMIO aperture. The memory space descriptor can come from the GCD memory space map, or it can represent a gap between two neighboring memory space descriptors. In the latter case, the GcdMemoryType field is expected to be EfiGcdMemoryTypeNonExistent. If the memory space descriptor already has type EfiGcdMemoryTypeMemoryMappedIo, and its capabilities are a superset of the required capabilities, then no action is taken -- it is by definition compatible with the aperture. Otherwise, the intersection of the memory space descriptor is calculated with the aperture. If the intersection is the empty set (no overlap), no action is taken; the memory space descriptor is compatible with the aperture. Otherwise, the type of the descriptor is investigated again. If the type is EfiGcdMemoryTypeNonExistent (representing a gap, or a genuine descriptor with such a type), then an attempt is made to add the intersection as MMIO space to the GCD memory space map, with the specified capabilities. This ensures continuity for the aperture, and the descriptor is deemed compatible with the aperture. Otherwise, the memory space descriptor is incompatible with the MMIO aperture. @param[in] Base Base address of the aperture. @param[in] Length Length of the aperture. @param[in] Capabilities Capabilities required by the aperture. @param[in] Descriptor The descriptor to ensure compatibility with the aperture for. @retval EFI_SUCCESS The descriptor is compatible. The GCD memory space map may have been updated, for continuity within the aperture. @retval EFI_INVALID_PARAMETER The descriptor is incompatible. @return Error codes from gDS->AddMemorySpace(). **/ EFI_STATUS IntersectMemoryDescriptor ( IN UINT64 Base, IN UINT64 Length, IN UINT64 Capabilities, IN CONST EFI_GCD_MEMORY_SPACE_DESCRIPTOR *Descriptor ) { UINT64 IntersectionBase; UINT64 IntersectionEnd; EFI_STATUS Status; if (Descriptor->GcdMemoryType == EfiGcdMemoryTypeMemoryMappedIo && (Descriptor->Capabilities & Capabilities) == Capabilities) { return EFI_SUCCESS; } IntersectionBase = MAX (Base, Descriptor->BaseAddress); IntersectionEnd = MIN (Base + Length, Descriptor->BaseAddress + Descriptor->Length); if (IntersectionBase >= IntersectionEnd) { // // The descriptor and the aperture don't overlap. // return EFI_SUCCESS; } if (Descriptor->GcdMemoryType == EfiGcdMemoryTypeNonExistent) { Status = gDS->AddMemorySpace (EfiGcdMemoryTypeMemoryMappedIo, IntersectionBase, IntersectionEnd - IntersectionBase, Capabilities); DEBUG ((EFI_ERROR (Status) ? EFI_D_ERROR : EFI_D_VERBOSE, "%a: %a: add [%Lx, %Lx): %r\n", gEfiCallerBaseName, __FUNCTION__, IntersectionBase, IntersectionEnd, Status)); return Status; } DEBUG ((EFI_D_ERROR, "%a: %a: desc [%Lx, %Lx) type %u cap %Lx conflicts " "with aperture [%Lx, %Lx) cap %Lx\n", gEfiCallerBaseName, __FUNCTION__, Descriptor->BaseAddress, Descriptor->BaseAddress + Descriptor->Length, (UINT32)Descriptor->GcdMemoryType, Descriptor->Capabilities, Base, Base + Length, Capabilities)); return EFI_INVALID_PARAMETER; } /** Add MMIO space to GCD. The routine checks the GCD database and only adds those which are not added in the specified range to GCD. @param Base Base address of the MMIO space. @param Length Length of the MMIO space. @param Capabilities Capabilities of the MMIO space. @retval EFI_SUCCES The MMIO space was added successfully. **/ EFI_STATUS AddMemoryMappedIoSpace ( IN UINT64 Base, IN UINT64 Length, IN UINT64 Capabilities ) { EFI_STATUS Status; UINTN Index; UINTN NumberOfDescriptors; EFI_GCD_MEMORY_SPACE_DESCRIPTOR *MemorySpaceMap; Status = gDS->GetMemorySpaceMap (&NumberOfDescriptors, &MemorySpaceMap); if (EFI_ERROR (Status)) { DEBUG ((EFI_D_ERROR, "%a: %a: GetMemorySpaceMap(): %r\n", gEfiCallerBaseName, __FUNCTION__, Status)); return Status; } for (Index = 0; Index < NumberOfDescriptors; Index++) { Status = IntersectMemoryDescriptor (Base, Length, Capabilities, &MemorySpaceMap[Index]); if (EFI_ERROR (Status)) { goto FreeMemorySpaceMap; } } DEBUG_CODE ( // // Make sure there are adjacent descriptors covering [Base, Base + Length). // It is possible that they have not been merged; merging can be prevented // by allocation and different capabilities. // UINT64 CheckBase; EFI_STATUS CheckStatus; EFI_GCD_MEMORY_SPACE_DESCRIPTOR Descriptor; for (CheckBase = Base; CheckBase < Base + Length; CheckBase = Descriptor.BaseAddress + Descriptor.Length) { CheckStatus = gDS->GetMemorySpaceDescriptor (CheckBase, &Descriptor); ASSERT_EFI_ERROR (CheckStatus); ASSERT (Descriptor.GcdMemoryType == EfiGcdMemoryTypeMemoryMappedIo); ASSERT ((Descriptor.Capabilities & Capabilities) == Capabilities); } ); FreeMemorySpaceMap: FreePool (MemorySpaceMap); return Status; } /** Event notification that is fired when IOMMU protocol is installed. @param Event The Event that is being processed. @param Context Event Context. **/ VOID EFIAPI IoMmuProtocolCallback ( IN EFI_EVENT Event, IN VOID *Context ) { EFI_STATUS Status; Status = gBS->LocateProtocol (&gEdkiiIoMmuProtocolGuid, NULL, (VOID **)&mIoMmu); if (!EFI_ERROR(Status)) { gBS->CloseEvent (mIoMmuEvent); } } /** Entry point of this driver. @param ImageHandle Image handle of this driver. @param SystemTable Pointer to standard EFI system table. @retval EFI_SUCCESS Succeed. @retval EFI_DEVICE_ERROR Fail to install PCI_ROOT_BRIDGE_IO protocol. **/ EFI_STATUS EFIAPI InitializePciHostBridge ( IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable ) { EFI_STATUS Status; PCI_HOST_BRIDGE_INSTANCE *HostBridge; PCI_ROOT_BRIDGE_INSTANCE *RootBridge; PCI_ROOT_BRIDGE *RootBridges; UINTN RootBridgeCount; UINTN Index; PCI_ROOT_BRIDGE_APERTURE *MemApertures[4]; UINTN MemApertureIndex; BOOLEAN ResourceAssigned; LIST_ENTRY *Link; UINT64 HostAddress; RootBridges = PciHostBridgeGetRootBridges (&RootBridgeCount); if ((RootBridges == NULL) || (RootBridgeCount == 0)) { return EFI_UNSUPPORTED; } Status = gBS->LocateProtocol (&gEfiCpuIo2ProtocolGuid, NULL, (VOID **) &mCpuIo); ASSERT_EFI_ERROR (Status); // // Most systems in the world including complex servers have only one Host Bridge. // HostBridge = AllocateZeroPool (sizeof (PCI_HOST_BRIDGE_INSTANCE)); ASSERT (HostBridge != NULL); HostBridge->Signature = PCI_HOST_BRIDGE_SIGNATURE; HostBridge->CanRestarted = TRUE; InitializeListHead (&HostBridge->RootBridges); ResourceAssigned = FALSE; // // Create Root Bridge Device Handle in this Host Bridge // for (Index = 0; Index < RootBridgeCount; Index++) { // // Create Root Bridge Handle Instance // RootBridge = CreateRootBridge (&RootBridges[Index]); ASSERT (RootBridge != NULL); if (RootBridge == NULL) { continue; } // // Make sure all root bridges share the same ResourceAssigned value. // if (Index == 0) { ResourceAssigned = RootBridges[Index].ResourceAssigned; } else { ASSERT (ResourceAssigned == RootBridges[Index].ResourceAssigned); } if (RootBridges[Index].Io.Base <= RootBridges[Index].Io.Limit) { // // Base and Limit in PCI_ROOT_BRIDGE_APERTURE are device address. // For GCD resource manipulation, we need to use host address. // HostAddress = TO_HOST_ADDRESS (RootBridges[Index].Io.Base, RootBridges[Index].Io.Translation); Status = AddIoSpace ( HostAddress, RootBridges[Index].Io.Limit - RootBridges[Index].Io.Base + 1 ); ASSERT_EFI_ERROR (Status); if (ResourceAssigned) { Status = gDS->AllocateIoSpace ( EfiGcdAllocateAddress, EfiGcdIoTypeIo, 0, RootBridges[Index].Io.Limit - RootBridges[Index].Io.Base + 1, &HostAddress, gImageHandle, NULL ); ASSERT_EFI_ERROR (Status); } } // // Add all the Mem/PMem aperture to GCD // Mem/PMem shouldn't overlap with each other // Root bridge which needs to combine MEM and PMEM should only report // the MEM aperture in Mem // MemApertures[0] = &RootBridges[Index].Mem; MemApertures[1] = &RootBridges[Index].MemAbove4G; MemApertures[2] = &RootBridges[Index].PMem; MemApertures[3] = &RootBridges[Index].PMemAbove4G; for (MemApertureIndex = 0; MemApertureIndex < ARRAY_SIZE (MemApertures); MemApertureIndex++) { if (MemApertures[MemApertureIndex]->Base <= MemApertures[MemApertureIndex]->Limit) { // // Base and Limit in PCI_ROOT_BRIDGE_APERTURE are device address. // For GCD resource manipulation, we need to use host address. // HostAddress = TO_HOST_ADDRESS (MemApertures[MemApertureIndex]->Base, MemApertures[MemApertureIndex]->Translation); Status = AddMemoryMappedIoSpace ( HostAddress, MemApertures[MemApertureIndex]->Limit - MemApertures[MemApertureIndex]->Base + 1, EFI_MEMORY_UC ); ASSERT_EFI_ERROR (Status); Status = gDS->SetMemorySpaceAttributes ( HostAddress, MemApertures[MemApertureIndex]->Limit - MemApertures[MemApertureIndex]->Base + 1, EFI_MEMORY_UC ); if (EFI_ERROR (Status)) { DEBUG ((DEBUG_WARN, "PciHostBridge driver failed to set EFI_MEMORY_UC to MMIO aperture - %r.\n", Status)); } if (ResourceAssigned) { Status = gDS->AllocateMemorySpace ( EfiGcdAllocateAddress, EfiGcdMemoryTypeMemoryMappedIo, 0, MemApertures[MemApertureIndex]->Limit - MemApertures[MemApertureIndex]->Base + 1, &HostAddress, gImageHandle, NULL ); ASSERT_EFI_ERROR (Status); } } } // // Insert Root Bridge Handle Instance // InsertTailList (&HostBridge->RootBridges, &RootBridge->Link); } // // When resources were assigned, it's not needed to expose // PciHostBridgeResourceAllocation protocol. // if (!ResourceAssigned) { HostBridge->ResAlloc.NotifyPhase = NotifyPhase; HostBridge->ResAlloc.GetNextRootBridge = GetNextRootBridge; HostBridge->ResAlloc.GetAllocAttributes = GetAttributes; HostBridge->ResAlloc.StartBusEnumeration = StartBusEnumeration; HostBridge->ResAlloc.SetBusNumbers = SetBusNumbers; HostBridge->ResAlloc.SubmitResources = SubmitResources; HostBridge->ResAlloc.GetProposedResources = GetProposedResources; HostBridge->ResAlloc.PreprocessController = PreprocessController; Status = gBS->InstallMultipleProtocolInterfaces ( &HostBridge->Handle, &gEfiPciHostBridgeResourceAllocationProtocolGuid, &HostBridge->ResAlloc, NULL ); ASSERT_EFI_ERROR (Status); } for (Link = GetFirstNode (&HostBridge->RootBridges) ; !IsNull (&HostBridge->RootBridges, Link) ; Link = GetNextNode (&HostBridge->RootBridges, Link) ) { RootBridge = ROOT_BRIDGE_FROM_LINK (Link); RootBridge->RootBridgeIo.ParentHandle = HostBridge->Handle; Status = gBS->InstallMultipleProtocolInterfaces ( &RootBridge->Handle, &gEfiDevicePathProtocolGuid, RootBridge->DevicePath, &gEfiPciRootBridgeIoProtocolGuid, &RootBridge->RootBridgeIo, NULL ); ASSERT_EFI_ERROR (Status); } PciHostBridgeFreeRootBridges (RootBridges, RootBridgeCount); if (!EFI_ERROR (Status)) { mIoMmuEvent = EfiCreateProtocolNotifyEvent ( &gEdkiiIoMmuProtocolGuid, TPL_CALLBACK, IoMmuProtocolCallback, NULL, &mIoMmuRegistration ); } return Status; } /** This routine constructs the resource descriptors for all root bridges and call PciHostBridgeResourceConflict(). @param HostBridge The Host Bridge Instance where the resource adjustment happens. **/ VOID ResourceConflict ( IN PCI_HOST_BRIDGE_INSTANCE *HostBridge ) { EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *Resources; EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *Descriptor; EFI_ACPI_END_TAG_DESCRIPTOR *End; PCI_ROOT_BRIDGE_INSTANCE *RootBridge; LIST_ENTRY *Link; UINTN RootBridgeCount; PCI_RESOURCE_TYPE Index; PCI_RES_NODE *ResAllocNode; RootBridgeCount = 0; for (Link = GetFirstNode (&HostBridge->RootBridges) ; !IsNull (&HostBridge->RootBridges, Link) ; Link = GetNextNode (&HostBridge->RootBridges, Link) ) { RootBridgeCount++; } Resources = AllocatePool ( RootBridgeCount * (TypeMax * sizeof (EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR) + sizeof (EFI_ACPI_END_TAG_DESCRIPTOR)) + sizeof (EFI_ACPI_END_TAG_DESCRIPTOR) ); ASSERT (Resources != NULL); for (Link = GetFirstNode (&HostBridge->RootBridges), Descriptor = Resources ; !IsNull (&HostBridge->RootBridges, Link) ; Link = GetNextNode (&HostBridge->RootBridges, Link) ) { RootBridge = ROOT_BRIDGE_FROM_LINK (Link); for (Index = TypeIo; Index < TypeMax; Index++) { ResAllocNode = &RootBridge->ResAllocNode[Index]; Descriptor->Desc = ACPI_ADDRESS_SPACE_DESCRIPTOR; Descriptor->Len = sizeof (EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR) - 3; Descriptor->AddrRangeMin = ResAllocNode->Base; Descriptor->AddrRangeMax = ResAllocNode->Alignment; Descriptor->AddrLen = ResAllocNode->Length; Descriptor->SpecificFlag = 0; switch (ResAllocNode->Type) { case TypeIo: Descriptor->ResType = ACPI_ADDRESS_SPACE_TYPE_IO; break; case TypePMem32: Descriptor->SpecificFlag = EFI_ACPI_MEMORY_RESOURCE_SPECIFIC_FLAG_CACHEABLE_PREFETCHABLE; case TypeMem32: Descriptor->ResType = ACPI_ADDRESS_SPACE_TYPE_MEM; Descriptor->AddrSpaceGranularity = 32; break; case TypePMem64: Descriptor->SpecificFlag = EFI_ACPI_MEMORY_RESOURCE_SPECIFIC_FLAG_CACHEABLE_PREFETCHABLE; case TypeMem64: Descriptor->ResType = ACPI_ADDRESS_SPACE_TYPE_MEM; Descriptor->AddrSpaceGranularity = 64; break; case TypeBus: Descriptor->ResType = ACPI_ADDRESS_SPACE_TYPE_BUS; break; default: break; } Descriptor++; } // // Terminate the root bridge resources. // End = (EFI_ACPI_END_TAG_DESCRIPTOR *) Descriptor; End->Desc = ACPI_END_TAG_DESCRIPTOR; End->Checksum = 0x0; Descriptor = (EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *) (End + 1); } // // Terminate the host bridge resources. // End = (EFI_ACPI_END_TAG_DESCRIPTOR *) Descriptor; End->Desc = ACPI_END_TAG_DESCRIPTOR; End->Checksum = 0x0; DEBUG ((DEBUG_ERROR, "Call PciHostBridgeResourceConflict().\n")); PciHostBridgeResourceConflict (HostBridge->Handle, Resources); FreePool (Resources); } /** Allocate Length of MMIO or IO resource with alignment BitsOfAlignment from GCD range [BaseAddress, Limit). @param Mmio TRUE for MMIO and FALSE for IO. @param Length Length of the resource to allocate. @param BitsOfAlignment Alignment of the resource to allocate. @param BaseAddress The starting address the allocation is from. @param Limit The ending address the allocation is to. @retval The base address of the allocated resource or MAX_UINT64 if allocation fails. **/ UINT64 AllocateResource ( BOOLEAN Mmio, UINT64 Length, UINTN BitsOfAlignment, UINT64 BaseAddress, UINT64 Limit ) { EFI_STATUS Status; if (BaseAddress < Limit) { // // Have to make sure Aligment is handled since we are doing direct address allocation // Strictly speaking, alignment requirement should be applied to device // address instead of host address which is used in GCD manipulation below, // but as we restrict the alignment of Translation to be larger than any BAR // alignment in the root bridge, we can simplify the situation and consider // the same alignment requirement is also applied to host address. // BaseAddress = ALIGN_VALUE (BaseAddress, LShiftU64 (1, BitsOfAlignment)); while (BaseAddress + Length <= Limit + 1) { if (Mmio) { Status = gDS->AllocateMemorySpace ( EfiGcdAllocateAddress, EfiGcdMemoryTypeMemoryMappedIo, BitsOfAlignment, Length, &BaseAddress, gImageHandle, NULL ); } else { Status = gDS->AllocateIoSpace ( EfiGcdAllocateAddress, EfiGcdIoTypeIo, BitsOfAlignment, Length, &BaseAddress, gImageHandle, NULL ); } if (!EFI_ERROR (Status)) { return BaseAddress; } BaseAddress += LShiftU64 (1, BitsOfAlignment); } } return MAX_UINT64; } /** Enter a certain phase of the PCI enumeration process. @param This The EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_PROTOCOL instance. @param Phase The phase during enumeration. @retval EFI_SUCCESS Succeed. @retval EFI_INVALID_PARAMETER Wrong phase parameter passed in. @retval EFI_NOT_READY Resources have not been submitted yet. **/ EFI_STATUS EFIAPI NotifyPhase ( IN EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_PROTOCOL *This, IN EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_PHASE Phase ) { PCI_HOST_BRIDGE_INSTANCE *HostBridge; PCI_ROOT_BRIDGE_INSTANCE *RootBridge; LIST_ENTRY *Link; EFI_PHYSICAL_ADDRESS BaseAddress; UINTN BitsOfAlignment; UINT64 Alignment; EFI_STATUS Status; EFI_STATUS ReturnStatus; PCI_RESOURCE_TYPE Index; PCI_RESOURCE_TYPE Index1; PCI_RESOURCE_TYPE Index2; BOOLEAN ResNodeHandled[TypeMax]; UINT64 MaxAlignment; UINT64 Translation; HostBridge = PCI_HOST_BRIDGE_FROM_THIS (This); switch (Phase) { case EfiPciHostBridgeBeginEnumeration: if (!HostBridge->CanRestarted) { return EFI_NOT_READY; } // // Reset Root Bridge // for (Link = GetFirstNode (&HostBridge->RootBridges) ; !IsNull (&HostBridge->RootBridges, Link) ; Link = GetNextNode (&HostBridge->RootBridges, Link) ) { RootBridge = ROOT_BRIDGE_FROM_LINK (Link); for (Index = TypeIo; Index < TypeMax; Index++) { RootBridge->ResAllocNode[Index].Type = Index; RootBridge->ResAllocNode[Index].Base = 0; RootBridge->ResAllocNode[Index].Length = 0; RootBridge->ResAllocNode[Index].Status = ResNone; RootBridge->ResourceSubmitted = FALSE; } } HostBridge->CanRestarted = TRUE; break; case EfiPciHostBridgeBeginBusAllocation: // // No specific action is required here, can perform any chipset specific programing // HostBridge->CanRestarted = FALSE; break; case EfiPciHostBridgeEndBusAllocation: // // No specific action is required here, can perform any chipset specific programing // break; case EfiPciHostBridgeBeginResourceAllocation: // // No specific action is required here, can perform any chipset specific programing // break; case EfiPciHostBridgeAllocateResources: ReturnStatus = EFI_SUCCESS; // // Make sure the resource for all root bridges has been submitted. // for (Link = GetFirstNode (&HostBridge->RootBridges) ; !IsNull (&HostBridge->RootBridges, Link) ; Link = GetNextNode (&HostBridge->RootBridges, Link) ) { RootBridge = ROOT_BRIDGE_FROM_LINK (Link); if (!RootBridge->ResourceSubmitted) { return EFI_NOT_READY; } } DEBUG ((EFI_D_INFO, "PciHostBridge: NotifyPhase (AllocateResources)\n")); for (Link = GetFirstNode (&HostBridge->RootBridges) ; !IsNull (&HostBridge->RootBridges, Link) ; Link = GetNextNode (&HostBridge->RootBridges, Link) ) { for (Index = TypeIo; Index < TypeBus; Index++) { ResNodeHandled[Index] = FALSE; } RootBridge = ROOT_BRIDGE_FROM_LINK (Link); DEBUG ((EFI_D_INFO, " RootBridge: %s\n", RootBridge->DevicePathStr)); for (Index1 = TypeIo; Index1 < TypeBus; Index1++) { if (RootBridge->ResAllocNode[Index1].Status == ResNone) { ResNodeHandled[Index1] = TRUE; } else { // // Allocate the resource node with max alignment at first // MaxAlignment = 0; Index = TypeMax; for (Index2 = TypeIo; Index2 < TypeBus; Index2++) { if (ResNodeHandled[Index2]) { continue; } if (MaxAlignment <= RootBridge->ResAllocNode[Index2].Alignment) { MaxAlignment = RootBridge->ResAllocNode[Index2].Alignment; Index = Index2; } } ASSERT (Index < TypeMax); ResNodeHandled[Index] = TRUE; Alignment = RootBridge->ResAllocNode[Index].Alignment; BitsOfAlignment = LowBitSet64 (Alignment + 1); BaseAddress = MAX_UINT64; // // RESTRICTION: To simplify the situation, we require the alignment of // Translation must be larger than any BAR alignment in the same root // bridge, so that resource allocation alignment can be applied to // both device address and host address. // Translation = GetTranslationByResourceType (RootBridge, Index); if ((Translation & Alignment) != 0) { DEBUG ((DEBUG_ERROR, "[%a:%d] Translation %lx is not aligned to %lx!\n", __FUNCTION__, __LINE__, Translation, Alignment )); ASSERT ((Translation & Alignment) == 0); // // This may be caused by too large alignment or too small // Translation; pick the 1st possibility and return out of resource, // which can also go thru the same process for out of resource // outside the loop. // ReturnStatus = EFI_OUT_OF_RESOURCES; continue; } switch (Index) { case TypeIo: // // Base and Limit in PCI_ROOT_BRIDGE_APERTURE are device address. // For AllocateResource is manipulating GCD resource, we need to use // host address here. // BaseAddress = AllocateResource ( FALSE, RootBridge->ResAllocNode[Index].Length, MIN (15, BitsOfAlignment), TO_HOST_ADDRESS (ALIGN_VALUE (RootBridge->Io.Base, Alignment + 1), RootBridge->Io.Translation), TO_HOST_ADDRESS (RootBridge->Io.Limit, RootBridge->Io.Translation) ); break; case TypeMem64: BaseAddress = AllocateResource ( TRUE, RootBridge->ResAllocNode[Index].Length, MIN (63, BitsOfAlignment), TO_HOST_ADDRESS (ALIGN_VALUE (RootBridge->MemAbove4G.Base, Alignment + 1), RootBridge->MemAbove4G.Translation), TO_HOST_ADDRESS (RootBridge->MemAbove4G.Limit, RootBridge->MemAbove4G.Translation) ); if (BaseAddress != MAX_UINT64) { break; } // // If memory above 4GB is not available, try memory below 4GB // case TypeMem32: BaseAddress = AllocateResource ( TRUE, RootBridge->ResAllocNode[Index].Length, MIN (31, BitsOfAlignment), TO_HOST_ADDRESS (ALIGN_VALUE (RootBridge->Mem.Base, Alignment + 1), RootBridge->Mem.Translation), TO_HOST_ADDRESS (RootBridge->Mem.Limit, RootBridge->Mem.Translation) ); break; case TypePMem64: BaseAddress = AllocateResource ( TRUE, RootBridge->ResAllocNode[Index].Length, MIN (63, BitsOfAlignment), TO_HOST_ADDRESS (ALIGN_VALUE (RootBridge->PMemAbove4G.Base, Alignment + 1), RootBridge->PMemAbove4G.Translation), TO_HOST_ADDRESS (RootBridge->PMemAbove4G.Limit, RootBridge->PMemAbove4G.Translation) ); if (BaseAddress != MAX_UINT64) { break; } // // If memory above 4GB is not available, try memory below 4GB // case TypePMem32: BaseAddress = AllocateResource ( TRUE, RootBridge->ResAllocNode[Index].Length, MIN (31, BitsOfAlignment), TO_HOST_ADDRESS (ALIGN_VALUE (RootBridge->PMem.Base, Alignment + 1), RootBridge->PMem.Translation), TO_HOST_ADDRESS (RootBridge->PMem.Limit, RootBridge->PMem.Translation) ); break; default: ASSERT (FALSE); break; } DEBUG ((DEBUG_INFO, " %s: Base/Length/Alignment = %lx/%lx/%lx - ", mPciResourceTypeStr[Index], BaseAddress, RootBridge->ResAllocNode[Index].Length, Alignment)); if (BaseAddress != MAX_UINT64) { RootBridge->ResAllocNode[Index].Base = BaseAddress; RootBridge->ResAllocNode[Index].Status = ResAllocated; DEBUG ((DEBUG_INFO, "Success\n")); } else { ReturnStatus = EFI_OUT_OF_RESOURCES; DEBUG ((DEBUG_ERROR, "Out Of Resource!\n")); } } } } if (ReturnStatus == EFI_OUT_OF_RESOURCES) { ResourceConflict (HostBridge); } // // Set resource to zero for nodes where allocation fails // for (Link = GetFirstNode (&HostBridge->RootBridges) ; !IsNull (&HostBridge->RootBridges, Link) ; Link = GetNextNode (&HostBridge->RootBridges, Link) ) { RootBridge = ROOT_BRIDGE_FROM_LINK (Link); for (Index = TypeIo; Index < TypeBus; Index++) { if (RootBridge->ResAllocNode[Index].Status != ResAllocated) { RootBridge->ResAllocNode[Index].Length = 0; } } } return ReturnStatus; case EfiPciHostBridgeSetResources: // // HostBridgeInstance->CanRestarted = FALSE; // break; case EfiPciHostBridgeFreeResources: // // HostBridgeInstance->CanRestarted = FALSE; // ReturnStatus = EFI_SUCCESS; for (Link = GetFirstNode (&HostBridge->RootBridges) ; !IsNull (&HostBridge->RootBridges, Link) ; Link = GetNextNode (&HostBridge->RootBridges, Link) ) { RootBridge = ROOT_BRIDGE_FROM_LINK (Link); for (Index = TypeIo; Index < TypeBus; Index++) { if (RootBridge->ResAllocNode[Index].Status == ResAllocated) { switch (Index) { case TypeIo: Status = gDS->FreeIoSpace (RootBridge->ResAllocNode[Index].Base, RootBridge->ResAllocNode[Index].Length); if (EFI_ERROR (Status)) { ReturnStatus = Status; } break; case TypeMem32: case TypePMem32: case TypeMem64: case TypePMem64: Status = gDS->FreeMemorySpace (RootBridge->ResAllocNode[Index].Base, RootBridge->ResAllocNode[Index].Length); if (EFI_ERROR (Status)) { ReturnStatus = Status; } break; default: ASSERT (FALSE); break; } RootBridge->ResAllocNode[Index].Type = Index; RootBridge->ResAllocNode[Index].Base = 0; RootBridge->ResAllocNode[Index].Length = 0; RootBridge->ResAllocNode[Index].Status = ResNone; } } RootBridge->ResourceSubmitted = FALSE; } HostBridge->CanRestarted = TRUE; return ReturnStatus; case EfiPciHostBridgeEndResourceAllocation: // // The resource allocation phase is completed. No specific action is required // here. This notification can be used to perform any chipset specific programming. // break; case EfiPciHostBridgeEndEnumeration: // // The Host Bridge Enumeration is completed. No specific action is required here. // This notification can be used to perform any chipset specific programming. // break; default: return EFI_INVALID_PARAMETER; } return EFI_SUCCESS; } /** Return the device handle of the next PCI root bridge that is associated with this Host Bridge. @param This The EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_ PROTOCOL instance. @param RootBridgeHandle Returns the device handle of the next PCI Root Bridge. On input, it holds the RootBridgeHandle returned by the most recent call to GetNextRootBridge().The handle for the first PCI Root Bridge is returned if RootBridgeHandle is NULL on input. @retval EFI_SUCCESS Succeed. @retval EFI_NOT_FOUND Next PCI root bridge not found. @retval EFI_INVALID_PARAMETER Wrong parameter passed in. **/ EFI_STATUS EFIAPI GetNextRootBridge ( IN EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_PROTOCOL *This, IN OUT EFI_HANDLE *RootBridgeHandle ) { BOOLEAN ReturnNext; LIST_ENTRY *Link; PCI_HOST_BRIDGE_INSTANCE *HostBridge; PCI_ROOT_BRIDGE_INSTANCE *RootBridge; if (RootBridgeHandle == NULL) { return EFI_INVALID_PARAMETER; } HostBridge = PCI_HOST_BRIDGE_FROM_THIS (This); ReturnNext = (BOOLEAN) (*RootBridgeHandle == NULL); for (Link = GetFirstNode (&HostBridge->RootBridges) ; !IsNull (&HostBridge->RootBridges, Link) ; Link = GetNextNode (&HostBridge->RootBridges, Link) ) { RootBridge = ROOT_BRIDGE_FROM_LINK (Link); if (ReturnNext) { *RootBridgeHandle = RootBridge->Handle; return EFI_SUCCESS; } ReturnNext = (BOOLEAN) (*RootBridgeHandle == RootBridge->Handle); } if (ReturnNext) { ASSERT (IsNull (&HostBridge->RootBridges, Link)); return EFI_NOT_FOUND; } else { return EFI_INVALID_PARAMETER; } } /** Returns the attributes of a PCI Root Bridge. @param This The EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_ PROTOCOL instance. @param RootBridgeHandle The device handle of the PCI Root Bridge that the caller is interested in. @param Attributes The pointer to attributes of the PCI Root Bridge. @retval EFI_SUCCESS Succeed. @retval EFI_INVALID_PARAMETER Attributes parameter passed in is NULL or RootBridgeHandle is not an EFI_HANDLE that was returned on a previous call to GetNextRootBridge(). **/ EFI_STATUS EFIAPI GetAttributes ( IN EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_PROTOCOL *This, IN EFI_HANDLE RootBridgeHandle, OUT UINT64 *Attributes ) { LIST_ENTRY *Link; PCI_HOST_BRIDGE_INSTANCE *HostBridge; PCI_ROOT_BRIDGE_INSTANCE *RootBridge; if (Attributes == NULL) { return EFI_INVALID_PARAMETER; } HostBridge = PCI_HOST_BRIDGE_FROM_THIS (This); for (Link = GetFirstNode (&HostBridge->RootBridges) ; !IsNull (&HostBridge->RootBridges, Link) ; Link = GetNextNode (&HostBridge->RootBridges, Link) ) { RootBridge = ROOT_BRIDGE_FROM_LINK (Link); if (RootBridgeHandle == RootBridge->Handle) { *Attributes = RootBridge->AllocationAttributes; return EFI_SUCCESS; } } return EFI_INVALID_PARAMETER; } /** This is the request from the PCI enumerator to set up the specified PCI Root Bridge for bus enumeration process. @param This The EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_ PROTOCOL instance. @param RootBridgeHandle The PCI Root Bridge to be set up. @param Configuration Pointer to the pointer to the PCI bus resource descriptor. @retval EFI_SUCCESS Succeed. @retval EFI_OUT_OF_RESOURCES Not enough pool to be allocated. @retval EFI_INVALID_PARAMETER RootBridgeHandle is not a valid handle. **/ EFI_STATUS EFIAPI StartBusEnumeration ( IN EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_PROTOCOL *This, IN EFI_HANDLE RootBridgeHandle, OUT VOID **Configuration ) { LIST_ENTRY *Link; PCI_HOST_BRIDGE_INSTANCE *HostBridge; PCI_ROOT_BRIDGE_INSTANCE *RootBridge; EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *Descriptor; EFI_ACPI_END_TAG_DESCRIPTOR *End; if (Configuration == NULL) { return EFI_INVALID_PARAMETER; } HostBridge = PCI_HOST_BRIDGE_FROM_THIS (This); for (Link = GetFirstNode (&HostBridge->RootBridges) ; !IsNull (&HostBridge->RootBridges, Link) ; Link = GetNextNode (&HostBridge->RootBridges, Link) ) { RootBridge = ROOT_BRIDGE_FROM_LINK (Link); if (RootBridgeHandle == RootBridge->Handle) { *Configuration = AllocatePool (sizeof (EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR) + sizeof (EFI_ACPI_END_TAG_DESCRIPTOR)); if (*Configuration == NULL) { return EFI_OUT_OF_RESOURCES; } Descriptor = (EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *) *Configuration; Descriptor->Desc = ACPI_ADDRESS_SPACE_DESCRIPTOR; Descriptor->Len = sizeof (EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR) - 3; Descriptor->ResType = ACPI_ADDRESS_SPACE_TYPE_BUS; Descriptor->GenFlag = 0; Descriptor->SpecificFlag = 0; Descriptor->AddrSpaceGranularity = 0; Descriptor->AddrRangeMin = RootBridge->Bus.Base; Descriptor->AddrRangeMax = 0; Descriptor->AddrTranslationOffset = 0; Descriptor->AddrLen = RootBridge->Bus.Limit - RootBridge->Bus.Base + 1; End = (EFI_ACPI_END_TAG_DESCRIPTOR *) (Descriptor + 1); End->Desc = ACPI_END_TAG_DESCRIPTOR; End->Checksum = 0x0; return EFI_SUCCESS; } } return EFI_INVALID_PARAMETER; } /** This function programs the PCI Root Bridge hardware so that it decodes the specified PCI bus range. @param This The EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_ PROTOCOL instance. @param RootBridgeHandle The PCI Root Bridge whose bus range is to be programmed. @param Configuration The pointer to the PCI bus resource descriptor. @retval EFI_SUCCESS Succeed. @retval EFI_INVALID_PARAMETER Wrong parameters passed in. **/ EFI_STATUS EFIAPI SetBusNumbers ( IN EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_PROTOCOL *This, IN EFI_HANDLE RootBridgeHandle, IN VOID *Configuration ) { LIST_ENTRY *Link; PCI_HOST_BRIDGE_INSTANCE *HostBridge; PCI_ROOT_BRIDGE_INSTANCE *RootBridge; EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *Descriptor; EFI_ACPI_END_TAG_DESCRIPTOR *End; if (Configuration == NULL) { return EFI_INVALID_PARAMETER; } Descriptor = (EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *) Configuration; End = (EFI_ACPI_END_TAG_DESCRIPTOR *) (Descriptor + 1); // // Check the Configuration is valid // if ((Descriptor->Desc != ACPI_ADDRESS_SPACE_DESCRIPTOR) || (Descriptor->ResType != ACPI_ADDRESS_SPACE_TYPE_BUS) || (End->Desc != ACPI_END_TAG_DESCRIPTOR) ) { return EFI_INVALID_PARAMETER; } HostBridge = PCI_HOST_BRIDGE_FROM_THIS (This); for (Link = GetFirstNode (&HostBridge->RootBridges) ; !IsNull (&HostBridge->RootBridges, Link) ; Link = GetNextNode (&HostBridge->RootBridges, Link) ) { RootBridge = ROOT_BRIDGE_FROM_LINK (Link); if (RootBridgeHandle == RootBridge->Handle) { if (Descriptor->AddrLen == 0) { return EFI_INVALID_PARAMETER; } if ((Descriptor->AddrRangeMin < RootBridge->Bus.Base) || (Descriptor->AddrRangeMin + Descriptor->AddrLen - 1 > RootBridge->Bus.Limit) ) { return EFI_INVALID_PARAMETER; } // // Update the Bus Range // RootBridge->ResAllocNode[TypeBus].Base = Descriptor->AddrRangeMin; RootBridge->ResAllocNode[TypeBus].Length = Descriptor->AddrLen; RootBridge->ResAllocNode[TypeBus].Status = ResAllocated; return EFI_SUCCESS; } } return EFI_INVALID_PARAMETER; } /** Submits the I/O and memory resource requirements for the specified PCI Root Bridge. @param This The EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_ PROTOCOL instance. @param RootBridgeHandle The PCI Root Bridge whose I/O and memory resource requirements. are being submitted. @param Configuration The pointer to the PCI I/O and PCI memory resource descriptor. @retval EFI_SUCCESS Succeed. @retval EFI_INVALID_PARAMETER Wrong parameters passed in. **/ EFI_STATUS EFIAPI SubmitResources ( IN EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_PROTOCOL *This, IN EFI_HANDLE RootBridgeHandle, IN VOID *Configuration ) { LIST_ENTRY *Link; PCI_HOST_BRIDGE_INSTANCE *HostBridge; PCI_ROOT_BRIDGE_INSTANCE *RootBridge; EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *Descriptor; PCI_RESOURCE_TYPE Type; // // Check the input parameter: Configuration // if (Configuration == NULL) { return EFI_INVALID_PARAMETER; } HostBridge = PCI_HOST_BRIDGE_FROM_THIS (This); for (Link = GetFirstNode (&HostBridge->RootBridges) ; !IsNull (&HostBridge->RootBridges, Link) ; Link = GetNextNode (&HostBridge->RootBridges, Link) ) { RootBridge = ROOT_BRIDGE_FROM_LINK (Link); if (RootBridgeHandle == RootBridge->Handle) { DEBUG ((EFI_D_INFO, "PciHostBridge: SubmitResources for %s\n", RootBridge->DevicePathStr)); // // Check the resource descriptors. // If the Configuration includes one or more invalid resource descriptors, all the resource // descriptors are ignored and the function returns EFI_INVALID_PARAMETER. // for (Descriptor = (EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *) Configuration; Descriptor->Desc == ACPI_ADDRESS_SPACE_DESCRIPTOR; Descriptor++) { if (Descriptor->ResType > ACPI_ADDRESS_SPACE_TYPE_BUS) { return EFI_INVALID_PARAMETER; } DEBUG ((EFI_D_INFO, " %s: Granularity/SpecificFlag = %ld / %02x%s\n", mAcpiAddressSpaceTypeStr[Descriptor->ResType], Descriptor->AddrSpaceGranularity, Descriptor->SpecificFlag, (Descriptor->SpecificFlag & EFI_ACPI_MEMORY_RESOURCE_SPECIFIC_FLAG_CACHEABLE_PREFETCHABLE) != 0 ? L" (Prefetchable)" : L"" )); DEBUG ((EFI_D_INFO, " Length/Alignment = 0x%lx / 0x%lx\n", Descriptor->AddrLen, Descriptor->AddrRangeMax)); switch (Descriptor->ResType) { case ACPI_ADDRESS_SPACE_TYPE_MEM: if (Descriptor->AddrSpaceGranularity != 32 && Descriptor->AddrSpaceGranularity != 64) { return EFI_INVALID_PARAMETER; } if (Descriptor->AddrSpaceGranularity == 32 && Descriptor->AddrLen >= SIZE_4GB) { return EFI_INVALID_PARAMETER; } // // If the PCI root bridge does not support separate windows for nonprefetchable and // prefetchable memory, then the PCI bus driver needs to include requests for // prefetchable memory in the nonprefetchable memory pool. // if (((RootBridge->AllocationAttributes & EFI_PCI_HOST_BRIDGE_COMBINE_MEM_PMEM) != 0) && ((Descriptor->SpecificFlag & EFI_ACPI_MEMORY_RESOURCE_SPECIFIC_FLAG_CACHEABLE_PREFETCHABLE) != 0) ) { return EFI_INVALID_PARAMETER; } case ACPI_ADDRESS_SPACE_TYPE_IO: // // Check aligment, it should be of the form 2^n-1 // if (GetPowerOfTwo64 (Descriptor->AddrRangeMax + 1) != (Descriptor->AddrRangeMax + 1)) { return EFI_INVALID_PARAMETER; } break; default: ASSERT (FALSE); break; } } if (Descriptor->Desc != ACPI_END_TAG_DESCRIPTOR) { return EFI_INVALID_PARAMETER; } for (Descriptor = (EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *) Configuration; Descriptor->Desc == ACPI_ADDRESS_SPACE_DESCRIPTOR; Descriptor++) { if (Descriptor->ResType == ACPI_ADDRESS_SPACE_TYPE_MEM) { if (Descriptor->AddrSpaceGranularity == 32) { if ((Descriptor->SpecificFlag & EFI_ACPI_MEMORY_RESOURCE_SPECIFIC_FLAG_CACHEABLE_PREFETCHABLE) != 0) { Type = TypePMem32; } else { Type = TypeMem32; } } else { ASSERT (Descriptor->AddrSpaceGranularity == 64); if ((Descriptor->SpecificFlag & EFI_ACPI_MEMORY_RESOURCE_SPECIFIC_FLAG_CACHEABLE_PREFETCHABLE) != 0) { Type = TypePMem64; } else { Type = TypeMem64; } } } else { ASSERT (Descriptor->ResType == ACPI_ADDRESS_SPACE_TYPE_IO); Type = TypeIo; } RootBridge->ResAllocNode[Type].Length = Descriptor->AddrLen; RootBridge->ResAllocNode[Type].Alignment = Descriptor->AddrRangeMax; RootBridge->ResAllocNode[Type].Status = ResSubmitted; } RootBridge->ResourceSubmitted = TRUE; return EFI_SUCCESS; } } return EFI_INVALID_PARAMETER; } /** This function returns the proposed resource settings for the specified PCI Root Bridge. @param This The EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_ PROTOCOL instance. @param RootBridgeHandle The PCI Root Bridge handle. @param Configuration The pointer to the pointer to the PCI I/O and memory resource descriptor. @retval EFI_SUCCESS Succeed. @retval EFI_OUT_OF_RESOURCES Not enough pool to be allocated. @retval EFI_INVALID_PARAMETER RootBridgeHandle is not a valid handle. **/ EFI_STATUS EFIAPI GetProposedResources ( IN EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_PROTOCOL *This, IN EFI_HANDLE RootBridgeHandle, OUT VOID **Configuration ) { LIST_ENTRY *Link; PCI_HOST_BRIDGE_INSTANCE *HostBridge; PCI_ROOT_BRIDGE_INSTANCE *RootBridge; UINTN Index; UINTN Number; VOID *Buffer; EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *Descriptor; EFI_ACPI_END_TAG_DESCRIPTOR *End; UINT64 ResStatus; HostBridge = PCI_HOST_BRIDGE_FROM_THIS (This); for (Link = GetFirstNode (&HostBridge->RootBridges) ; !IsNull (&HostBridge->RootBridges, Link) ; Link = GetNextNode (&HostBridge->RootBridges, Link) ) { RootBridge = ROOT_BRIDGE_FROM_LINK (Link); if (RootBridgeHandle == RootBridge->Handle) { for (Index = 0, Number = 0; Index < TypeBus; Index++) { if (RootBridge->ResAllocNode[Index].Status != ResNone) { Number++; } } Buffer = AllocateZeroPool (Number * sizeof (EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR) + sizeof (EFI_ACPI_END_TAG_DESCRIPTOR)); if (Buffer == NULL) { return EFI_OUT_OF_RESOURCES; } Descriptor = (EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *) Buffer; for (Index = 0; Index < TypeBus; Index++) { ResStatus = RootBridge->ResAllocNode[Index].Status; if (ResStatus != ResNone) { Descriptor->Desc = ACPI_ADDRESS_SPACE_DESCRIPTOR; Descriptor->Len = sizeof (EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR) - 3;; Descriptor->GenFlag = 0; // // AddrRangeMin in Resource Descriptor here should be device address // instead of host address, or else PCI bus driver cannot set correct // address into PCI BAR registers. // Base in ResAllocNode is a host address, so conversion is needed. // Descriptor->AddrRangeMin = TO_DEVICE_ADDRESS (RootBridge->ResAllocNode[Index].Base, GetTranslationByResourceType (RootBridge, Index)); Descriptor->AddrRangeMax = 0; Descriptor->AddrTranslationOffset = (ResStatus == ResAllocated) ? EFI_RESOURCE_SATISFIED : PCI_RESOURCE_LESS; Descriptor->AddrLen = RootBridge->ResAllocNode[Index].Length; switch (Index) { case TypeIo: Descriptor->ResType = ACPI_ADDRESS_SPACE_TYPE_IO; break; case TypePMem32: Descriptor->SpecificFlag = EFI_ACPI_MEMORY_RESOURCE_SPECIFIC_FLAG_CACHEABLE_PREFETCHABLE; case TypeMem32: Descriptor->ResType = ACPI_ADDRESS_SPACE_TYPE_MEM; Descriptor->AddrSpaceGranularity = 32; break; case TypePMem64: Descriptor->SpecificFlag = EFI_ACPI_MEMORY_RESOURCE_SPECIFIC_FLAG_CACHEABLE_PREFETCHABLE; case TypeMem64: Descriptor->ResType = ACPI_ADDRESS_SPACE_TYPE_MEM; Descriptor->AddrSpaceGranularity = 64; break; } Descriptor++; } } End = (EFI_ACPI_END_TAG_DESCRIPTOR *) Descriptor; End->Desc = ACPI_END_TAG_DESCRIPTOR; End->Checksum = 0; *Configuration = Buffer; return EFI_SUCCESS; } } return EFI_INVALID_PARAMETER; } /** This function is called for all the PCI controllers that the PCI bus driver finds. Can be used to Preprogram the controller. @param This The EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_ PROTOCOL instance. @param RootBridgeHandle The PCI Root Bridge handle. @param PciAddress Address of the controller on the PCI bus. @param Phase The Phase during resource allocation. @retval EFI_SUCCESS Succeed. @retval EFI_INVALID_PARAMETER RootBridgeHandle is not a valid handle. **/ EFI_STATUS EFIAPI PreprocessController ( IN EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_PROTOCOL *This, IN EFI_HANDLE RootBridgeHandle, IN EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_PCI_ADDRESS PciAddress, IN EFI_PCI_CONTROLLER_RESOURCE_ALLOCATION_PHASE Phase ) { LIST_ENTRY *Link; PCI_HOST_BRIDGE_INSTANCE *HostBridge; PCI_ROOT_BRIDGE_INSTANCE *RootBridge; if ((UINT32) Phase > EfiPciBeforeResourceCollection) { return EFI_INVALID_PARAMETER; } HostBridge = PCI_HOST_BRIDGE_FROM_THIS (This); for (Link = GetFirstNode (&HostBridge->RootBridges) ; !IsNull (&HostBridge->RootBridges, Link) ; Link = GetNextNode (&HostBridge->RootBridges, Link) ) { RootBridge = ROOT_BRIDGE_FROM_LINK (Link); if (RootBridgeHandle == RootBridge->Handle) { return EFI_SUCCESS; } } return EFI_INVALID_PARAMETER; }