device: Enable resource allocation above 4G boundary

This change adds support for allocating resources above the 4G
boundary by making use of memranges for resource windows enabled in
the previous CL.

It adds a new resource flag IORESOURCE_ABOVE_4G which is used in the
following ways:
a) Downstream device resources can set this flag to indicate that they
would like to have their resource allocation above the 4G
boundary. These semantics will have to be enabled in the drivers
managing the devices. It can also be extended to be enabled via
devicetree. This flag is automatically propagated by the resource
allocator from downstream devices to the upstream bridges in pass
1. It is done to ensure that the resource allocator has a global view
of downstream requirements during pass 2 at domain level.

b) Bridges have a single resource window for each of mem and prefmem
resource types. Thus, if any downstream resource of the bridge
requests allocation above 4G boundary, all the other downstream
resources of the same type under the bridge will be allocated above 4G
boundary.

c) During pass 2, resource allocator at domain level splits
IORESOURCE_MEM into two different memory ranges -- one for the window
below 4G and other above 4G. Resource allocation happens separately
for each of these windows.

d) At the bridge level, there is no extra logic required since the
resource will live entirely above or below the 4G boundary. Hence, all
downstream devices of any bridge will fall within the window allocated
to the bridge resource. To handle this case separately from that of
domain, initializing of memranges for a bridge is done differently
than the domain.

Limitation:
Resources of a given type at the bridge or downstream devices
cannot live both above and below 4G boundary. Thus, if a bridge has
some downstream resources requesting allocation for a given type above
4G boundary and other resources of the same type requesting allocation
below 4G boundary, then all these resources of the same type get
allocated above 4G boundary.

BUG=b:149186922
TEST=Verified that resources get allocated above the 4G boundary
correctly on volteer.

Signed-off-by: Furquan Shaikh <furquan@google.com>
Change-Id: I7fb2a75cc280a307300d29ddabaebfc49175548f
Reviewed-on: https://review.coreboot.org/c/coreboot/+/39487
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Aaron Durbin <adurbin@chromium.org>
This commit is contained in:
Furquan Shaikh 2020-03-31 21:21:52 -07:00 committed by Patrick Georgi
parent 3b02006afe
commit 44ae0eacb8
2 changed files with 113 additions and 9 deletions

View File

@ -593,6 +593,19 @@ static void update_bridge_resource(const struct device *bridge, struct resource
if (child_res->limit && (child_res->limit < bridge_res->limit))
bridge_res->limit = child_res->limit;
/*
* Propagate the downstream resource request to allocate above 4G boundary to
* upstream bridge resource. This ensures that during pass 2, the resource
* allocator at domain level has a global view of all the downstream device
* requirements and thus address space is allocated as per updated flags in the
* bridge resource.
*
* Since the bridge resource is a single window, all the downstream resources of
* this bridge resource will be allocated space above 4G boundary.
*/
if (child_res->flags & IORESOURCE_ABOVE_4G)
bridge_res->flags |= IORESOURCE_ABOVE_4G;
/*
* Alignment value of 0 means that the child resource has no alignment
* requirements and so the base value remains unchanged here.
@ -687,24 +700,98 @@ static void compute_domain_resources(const struct device *domain)
}
}
static void initialize_memranges(struct memranges *ranges, const struct resource *res,
unsigned long memrange_type)
/*
* If the resource base is set to the limit, then it means that the resource is invalid and
* hence cannot be used for allocation.
*/
static bool is_resource_invalid(const struct resource *res)
{
return res->base == res->limit;
}
/*
* This function initializes memranges for domain device. If the resource crosses 4G boundary,
* then this function splits it into two ranges -- one for the window below 4G and the other for
* the window above 4G. The latter range has IORESOURCE_ABOVE_4G flag set to satisfy resource
* requests from downstream devices for allocations above 4G.
*/
static void initialize_domain_memranges(struct memranges *ranges, const struct resource *res,
unsigned long memrange_type)
{
resource_t res_base;
resource_t res_limit;
const resource_t limit_4g = 0xffffffff;
memranges_init_empty(ranges, NULL, 0);
if (res == NULL)
if ((res == NULL) || is_resource_invalid(res))
return;
res_base = res->base;
res_limit = res->limit;
if (res_base == res_limit)
/*
* Split the resource into two separate ranges if it crosses the 4G boundary. Memrange
* type is set differently to ensure that memrange does not merge these two ranges. For
* the range above 4G boundary, given memrange type is ORed with IORESOURCE_ABOVE_4G.
*/
if (res_base <= limit_4g) {
resource_t range_limit;
/* Clip the resource limit at 4G boundary if necessary. */
range_limit = MIN(res_limit, limit_4g);
memranges_insert(ranges, res_base, range_limit - res_base + 1, memrange_type);
/*
* If the resource lies completely below the 4G boundary, nothing more needs to
* be done.
*/
if (res_limit <= limit_4g)
return;
/*
* If the resource window crosses the 4G boundary, then update res_base to add
* another entry for the range above the boundary.
*/
res_base = limit_4g + 1;
}
if (res_base > res_limit)
return;
memranges_insert(ranges, res_base, res_limit - res_base + 1, memrange_type);
/*
* If resource lies completely above the 4G boundary or if the resource was clipped to
* add two separate ranges, the range above 4G boundary has the resource flag
* IORESOURCE_ABOVE_4G set. This allows domain to handle any downstream requests for
* resource allocation above 4G differently.
*/
memranges_insert(ranges, res_base, res_limit - res_base + 1,
memrange_type | IORESOURCE_ABOVE_4G);
}
/*
* This function initializes memranges for bridge device. Unlike domain, bridge does not need to
* care about resource window crossing 4G boundary. This is handled by the resource allocator at
* domain level to ensure that all downstream bridges are allocated space either above or below
* 4G boundary as per the state of IORESOURCE_ABOVE_4G for the respective bridge resource.
*
* So, this function creates a single range of the entire resource window available for the
* bridge resource. Thus all downstream resources of the bridge for the given resource type get
* allocated space from the same window. If there is any downstream resource of the bridge which
* requests allocation above 4G, then all other downstream resources of the same type under the
* bridge get allocated above 4G.
*/
static void initialize_bridge_memranges(struct memranges *ranges, const struct resource *res,
unsigned long memrange_type)
{
memranges_init_empty(ranges, NULL, 0);
if ((res == NULL) || is_resource_invalid(res))
return;
memranges_insert(ranges, res->base, res->limit - res->base + 1, memrange_type);
}
static void print_resource_ranges(const struct memranges *ranges)
@ -834,10 +921,12 @@ static void setup_resource_ranges(const struct device *dev, const struct resourc
dev_path(dev), resource2str(res), res->base, res->size, res->align,
res->gran, res->limit);
initialize_memranges(ranges, res, type);
if (dev->path.type == DEVICE_PATH_DOMAIN)
if (dev->path.type == DEVICE_PATH_DOMAIN) {
initialize_domain_memranges(ranges, res, type);
constrain_domain_resources(dev->link_list, ranges, type);
} else {
initialize_bridge_memranges(ranges, res, type);
}
print_resource_ranges(ranges);
}
@ -943,12 +1032,25 @@ static void allocate_domain_resources(const struct device *domain)
* Domain does not distinguish between mem and prefmem resources. Thus, the resource
* allocation at domain level considers mem and prefmem together when finding the best
* fit based on the biggest resource requirement.
*
* However, resource requests for allocation above 4G boundary need to be handled
* separately if the domain resource window crosses this boundary. There is a single
* window for resource of type IORESOURCE_MEM. When creating memranges, this resource
* is split into two separate ranges -- one for the window below 4G boundary and other
* for the window above 4G boundary (with IORESOURCE_ABOVE_4G flag set). Thus, when
* allocating child resources, requests for below and above the 4G boundary are handled
* separately by setting the type_mask and type_match to allocate_child_resources()
* accordingly.
*/
res = find_domain_resource(domain, IORESOURCE_MEM);
if (res) {
setup_resource_ranges(domain, res, IORESOURCE_MEM, &ranges);
allocate_child_resources(domain->link_list, &ranges, IORESOURCE_TYPE_MASK,
allocate_child_resources(domain->link_list, &ranges,
IORESOURCE_TYPE_MASK | IORESOURCE_ABOVE_4G,
IORESOURCE_MEM);
allocate_child_resources(domain->link_list, &ranges,
IORESOURCE_TYPE_MASK | IORESOURCE_ABOVE_4G,
IORESOURCE_MEM | IORESOURCE_ABOVE_4G);
cleanup_resource_ranges(domain, &ranges, res);
}

View File

@ -24,6 +24,8 @@
#define IORESOURCE_SUBTRACTIVE 0x00040000
/* The IO resource has a bus below it. */
#define IORESOURCE_BRIDGE 0x00080000
/* This is a request to allocate resource about 4G boundary. */
#define IORESOURCE_ABOVE_4G 0x00100000
/* The resource needs to be reserved in the coreboot table */
#define IORESOURCE_RESERVE 0x10000000
/* The IO resource assignment has been stored in the device */