device: Enable resource allocation above 4G boundary with allocator v4

This change adds back CB:39487 which was reverted as part of
CB:41412. Now that the resource allocator is split into old(v3) and
new(v4), this change adds support for allocating resources above 4G
boundary with the new allocator v4.

Original commit message:
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.

Change-Id: I92a5cf7cd1457f2f713e1ffd8ea31796ce3d0cce
Signed-off-by: Furquan Shaikh <furquan@google.com>
Reviewed-on: https://review.coreboot.org/c/coreboot/+/41466
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Aaron Durbin <adurbin@chromium.org>
This commit is contained in:
Furquan Shaikh 2020-05-15 17:33:52 -07:00 committed by Patrick Georgi
parent c351f57dcc
commit 1bb05ef30b
2 changed files with 139 additions and 12 deletions

View File

@ -103,6 +103,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.
@ -207,22 +220,119 @@ static unsigned char get_alignment_by_resource_type(const struct resource *res)
die("Unexpected resource type: flags(%d)!\n", res->flags);
}
static void initialize_memranges(struct memranges *ranges, const struct resource *res,
/*
* If the resource is NULL or if the resource is not assigned, then it cannot be used for
* allocation for downstream devices.
*/
static bool is_resource_invalid(const struct resource *res)
{
return (res == NULL) || !(res->flags & IORESOURCE_ASSIGNED);
}
static void initialize_domain_io_resource_memranges(struct memranges *ranges,
const struct resource *res,
unsigned long memrange_type)
{
memranges_insert(ranges, res->base, res->limit - res->base + 1, memrange_type);
}
static void initialize_domain_mem_resource_memranges(struct memranges *ranges,
const struct resource *res,
unsigned long memrange_type)
{
resource_t res_base;
resource_t res_limit;
unsigned char align = get_alignment_by_resource_type(res);
memranges_init_empty_with_alignment(ranges, NULL, 0, align);
if ((res == NULL) || !(res->flags & IORESOURCE_ASSIGNED))
return;
const resource_t limit_4g = 0xffffffff;
res_base = res->base;
res_limit = res->limit;
memranges_insert(ranges, res_base, res_limit - res_base + 1, memrange_type);
/*
* 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;
/*
* 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 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)
{
unsigned char align = get_alignment_by_resource_type(res);
memranges_init_empty_with_alignment(ranges, NULL, 0, align);
if (is_resource_invalid(res))
return;
if (res->flags & IORESOURCE_IO)
initialize_domain_io_resource_memranges(ranges, res, memrange_type);
else
initialize_domain_mem_resource_memranges(ranges, res, memrange_type);
}
/*
* 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)
{
unsigned char align = get_alignment_by_resource_type(res);
memranges_init_empty_with_alignment(ranges, NULL, 0, align);
if (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)
@ -360,10 +470,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, ranges, type);
} else {
initialize_bridge_memranges(ranges, res, type);
}
print_resource_ranges(ranges);
}
@ -469,12 +581,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 */