Message ID | 20200501161014.5935-3-ardb@kernel.org (mailing list archive) |
---|---|
State | Mainlined |
Commit | 539979b6ec62f7bba40b0452b0574a0f4ec4fe4e |
Headers | show |
Series | ACPI/IORT: rework num_ids off-by-one quirk | expand |
On 2020/5/2 0:10, Ard Biesheuvel wrote: > The ID mapping table structure of the IORT table describes the size of > a range using a num_ids field carrying the number of IDs in the region > minus one. This has been misinterpreted in the past in the parsing code, > and firmware is known to have shipped where this results in an ambiguity, > where regions that should be adjacent have an overlap of one value. > > So let's work around this by detecting this case specifically: when > resolving an ID translation, allow one that matches right at the end of > a multi-ID region to be superseded by a subsequent one. > > To prevent potential regressions on broken firmware that happened to > work before, only take the subsequent match into account if it occurs > at the start of a mapping region. > > Signed-off-by: Ard Biesheuvel <ardb@kernel.org> > --- > drivers/acpi/arm64/iort.c | 40 +++++++++++++++++--- > 1 file changed, 34 insertions(+), 6 deletions(-) > > diff --git a/drivers/acpi/arm64/iort.c b/drivers/acpi/arm64/iort.c > index 98be18266a73..9f139a94a1d3 100644 > --- a/drivers/acpi/arm64/iort.c > +++ b/drivers/acpi/arm64/iort.c > @@ -300,7 +300,7 @@ static acpi_status iort_match_node_callback(struct acpi_iort_node *node, > } > > static int iort_id_map(struct acpi_iort_id_mapping *map, u8 type, u32 rid_in, > - u32 *rid_out) > + u32 *rid_out, bool check_overlap) > { > /* Single mapping does not care for input id */ > if (map->flags & ACPI_IORT_ID_SINGLE_MAPPING) { > @@ -316,10 +316,34 @@ static int iort_id_map(struct acpi_iort_id_mapping *map, u8 type, u32 rid_in, > } > > if (rid_in < map->input_base || > - (rid_in >= map->input_base + map->id_count)) > + (rid_in > map->input_base + map->id_count)) > return -ENXIO; > > + if (check_overlap) { > + /* > + * We already found a mapping for this input ID at the end of > + * another region. If it coincides with the start of this > + * region, we assume the prior match was due to the off-by-1 > + * issue mentioned below, and allow it to be superseded. > + * Otherwise, things are *really* broken, and we just disregard > + * duplicate matches entirely to retain compatibility. > + */ > + pr_err(FW_BUG "[map %p] conflicting mapping for input ID 0x%x\n", > + map, rid_in); As we already applied a workaround here, can we add "applying workaround" in the error message? This will make the customers less uneasy to see such message in the boot log. Once the product was deliveried to customers, it's not that easy to update all the firmwares entirely. I'm out of reach for D05/D06 hardware as it's holidays in China, please allow me to test this patch set in Wednesday this week. Thanks Hanjun
On Mon, 4 May 2020 at 06:32, Hanjun Guo <guohanjun@huawei.com> wrote: > > On 2020/5/2 0:10, Ard Biesheuvel wrote: > > The ID mapping table structure of the IORT table describes the size of > > a range using a num_ids field carrying the number of IDs in the region > > minus one. This has been misinterpreted in the past in the parsing code, > > and firmware is known to have shipped where this results in an ambiguity, > > where regions that should be adjacent have an overlap of one value. > > > > So let's work around this by detecting this case specifically: when > > resolving an ID translation, allow one that matches right at the end of > > a multi-ID region to be superseded by a subsequent one. > > > > To prevent potential regressions on broken firmware that happened to > > work before, only take the subsequent match into account if it occurs > > at the start of a mapping region. > > > > Signed-off-by: Ard Biesheuvel <ardb@kernel.org> > > --- > > drivers/acpi/arm64/iort.c | 40 +++++++++++++++++--- > > 1 file changed, 34 insertions(+), 6 deletions(-) > > > > diff --git a/drivers/acpi/arm64/iort.c b/drivers/acpi/arm64/iort.c > > index 98be18266a73..9f139a94a1d3 100644 > > --- a/drivers/acpi/arm64/iort.c > > +++ b/drivers/acpi/arm64/iort.c > > @@ -300,7 +300,7 @@ static acpi_status iort_match_node_callback(struct acpi_iort_node *node, > > } > > > > static int iort_id_map(struct acpi_iort_id_mapping *map, u8 type, u32 rid_in, > > - u32 *rid_out) > > + u32 *rid_out, bool check_overlap) > > { > > /* Single mapping does not care for input id */ > > if (map->flags & ACPI_IORT_ID_SINGLE_MAPPING) { > > @@ -316,10 +316,34 @@ static int iort_id_map(struct acpi_iort_id_mapping *map, u8 type, u32 rid_in, > > } > > > > if (rid_in < map->input_base || > > - (rid_in >= map->input_base + map->id_count)) > > + (rid_in > map->input_base + map->id_count)) > > return -ENXIO; > > > > + if (check_overlap) { > > + /* > > + * We already found a mapping for this input ID at the end of > > + * another region. If it coincides with the start of this > > + * region, we assume the prior match was due to the off-by-1 > > + * issue mentioned below, and allow it to be superseded. > > + * Otherwise, things are *really* broken, and we just disregard > > + * duplicate matches entirely to retain compatibility. > > + */ > > + pr_err(FW_BUG "[map %p] conflicting mapping for input ID 0x%x\n", > > + map, rid_in); > > As we already applied a workaround here, can we add "applying > workaround" in the error message? This will make the customers > less uneasy to see such message in the boot log. Once the product > was deliveried to customers, it's not that easy to update all the > firmwares entirely. > Sure. > I'm out of reach for D05/D06 hardware as it's holidays in China, > please allow me to test this patch set in Wednesday this week. > Yes please
On Fri, May 01, 2020 at 06:10:14PM +0200, Ard Biesheuvel wrote: > The ID mapping table structure of the IORT table describes the size of > a range using a num_ids field carrying the number of IDs in the region > minus one. This has been misinterpreted in the past in the parsing code, > and firmware is known to have shipped where this results in an ambiguity, > where regions that should be adjacent have an overlap of one value. > > So let's work around this by detecting this case specifically: when > resolving an ID translation, allow one that matches right at the end of > a multi-ID region to be superseded by a subsequent one. > > To prevent potential regressions on broken firmware that happened to > work before, only take the subsequent match into account if it occurs > at the start of a mapping region. > > Signed-off-by: Ard Biesheuvel <ardb@kernel.org> > --- > drivers/acpi/arm64/iort.c | 40 +++++++++++++++++--- > 1 file changed, 34 insertions(+), 6 deletions(-) The patch logic is sound - I still think that the resulting code can benefit from a one-off boot time mapping data initialisation but we can address that later as a clean-up, first thing is to remove the quirk mechanism. Goes without saying, this needs extensive testing on existing platforms before sending it to stable kernels. Reviewed-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com> > diff --git a/drivers/acpi/arm64/iort.c b/drivers/acpi/arm64/iort.c > index 98be18266a73..9f139a94a1d3 100644 > --- a/drivers/acpi/arm64/iort.c > +++ b/drivers/acpi/arm64/iort.c > @@ -300,7 +300,7 @@ static acpi_status iort_match_node_callback(struct acpi_iort_node *node, > } > > static int iort_id_map(struct acpi_iort_id_mapping *map, u8 type, u32 rid_in, > - u32 *rid_out) > + u32 *rid_out, bool check_overlap) > { > /* Single mapping does not care for input id */ > if (map->flags & ACPI_IORT_ID_SINGLE_MAPPING) { > @@ -316,10 +316,34 @@ static int iort_id_map(struct acpi_iort_id_mapping *map, u8 type, u32 rid_in, > } > > if (rid_in < map->input_base || > - (rid_in >= map->input_base + map->id_count)) > + (rid_in > map->input_base + map->id_count)) > return -ENXIO; > > + if (check_overlap) { > + /* > + * We already found a mapping for this input ID at the end of > + * another region. If it coincides with the start of this > + * region, we assume the prior match was due to the off-by-1 > + * issue mentioned below, and allow it to be superseded. > + * Otherwise, things are *really* broken, and we just disregard > + * duplicate matches entirely to retain compatibility. > + */ > + pr_err(FW_BUG "[map %p] conflicting mapping for input ID 0x%x\n", > + map, rid_in); > + if (rid_in != map->input_base) > + return -ENXIO; > + } > + > *rid_out = map->output_base + (rid_in - map->input_base); > + > + /* > + * Due to confusion regarding the meaning of the id_count field (which > + * carries the number of IDs *minus 1*), we may have to disregard this > + * match if it is at the end of the range, and overlaps with the start > + * of another one. > + */ > + if (map->id_count > 0 && rid_in == map->input_base + map->id_count) > + return -EAGAIN; > return 0; > } > > @@ -404,7 +428,8 @@ static struct acpi_iort_node *iort_node_map_id(struct acpi_iort_node *node, > /* Parse the ID mapping tree to find specified node type */ > while (node) { > struct acpi_iort_id_mapping *map; > - int i, index; > + int i, index, rc = 0; > + u32 out_ref = 0, map_id = id; > > if (IORT_TYPE_MASK(node->type) & type_mask) { > if (id_out) > @@ -438,15 +463,18 @@ static struct acpi_iort_node *iort_node_map_id(struct acpi_iort_node *node, > if (i == index) > continue; > > - if (!iort_id_map(map, node->type, id, &id)) > + rc = iort_id_map(map, node->type, map_id, &id, out_ref); > + if (!rc) > break; > + if (rc == -EAGAIN) > + out_ref = map->output_reference; > } > > - if (i == node->mapping_count) > + if (i == node->mapping_count && !out_ref) > goto fail_map; > > node = ACPI_ADD_PTR(struct acpi_iort_node, iort_table, > - map->output_reference); > + rc ? out_ref : map->output_reference); > } > > fail_map: > -- > 2.17.1 >
On 2020/5/4 12:32, Hanjun Guo wrote: > On 2020/5/2 0:10, Ard Biesheuvel wrote: >> The ID mapping table structure of the IORT table describes the size of >> a range using a num_ids field carrying the number of IDs in the region >> minus one. This has been misinterpreted in the past in the parsing code, >> and firmware is known to have shipped where this results in an ambiguity, >> where regions that should be adjacent have an overlap of one value. >> >> So let's work around this by detecting this case specifically: when >> resolving an ID translation, allow one that matches right at the end of >> a multi-ID region to be superseded by a subsequent one. >> >> To prevent potential regressions on broken firmware that happened to >> work before, only take the subsequent match into account if it occurs >> at the start of a mapping region. >> >> Signed-off-by: Ard Biesheuvel <ardb@kernel.org> >> --- >> drivers/acpi/arm64/iort.c | 40 +++++++++++++++++--- >> 1 file changed, 34 insertions(+), 6 deletions(-) >> >> diff --git a/drivers/acpi/arm64/iort.c b/drivers/acpi/arm64/iort.c >> index 98be18266a73..9f139a94a1d3 100644 >> --- a/drivers/acpi/arm64/iort.c >> +++ b/drivers/acpi/arm64/iort.c >> @@ -300,7 +300,7 @@ static acpi_status iort_match_node_callback(struct >> acpi_iort_node *node, >> } >> static int iort_id_map(struct acpi_iort_id_mapping *map, u8 type, >> u32 rid_in, >> - u32 *rid_out) >> + u32 *rid_out, bool check_overlap) >> { >> /* Single mapping does not care for input id */ >> if (map->flags & ACPI_IORT_ID_SINGLE_MAPPING) { >> @@ -316,10 +316,34 @@ static int iort_id_map(struct >> acpi_iort_id_mapping *map, u8 type, u32 rid_in, >> } >> if (rid_in < map->input_base || >> - (rid_in >= map->input_base + map->id_count)) >> + (rid_in > map->input_base + map->id_count)) >> return -ENXIO; >> + if (check_overlap) { >> + /* >> + * We already found a mapping for this input ID at the end of >> + * another region. If it coincides with the start of this >> + * region, we assume the prior match was due to the off-by-1 >> + * issue mentioned below, and allow it to be superseded. >> + * Otherwise, things are *really* broken, and we just disregard >> + * duplicate matches entirely to retain compatibility. >> + */ >> + pr_err(FW_BUG "[map %p] conflicting mapping for input ID >> 0x%x\n", >> + map, rid_in); > > As we already applied a workaround here, can we add "applying > workaround" in the error message? This will make the customers > less uneasy to see such message in the boot log. Once the product > was deliveried to customers, it's not that easy to update all the > firmwares entirely. > > I'm out of reach for D05/D06 hardware as it's holidays in China, > please allow me to test this patch set in Wednesday this week. Tested this patchset on D06 and I comapared each sucessful mapped ID, no regressions, and also no function regressions as well, Tested-by: Hanjun Guo <guohanjun@huawei.com> Thanks Hanjun
On 2020/5/4 15:36, Ard Biesheuvel wrote: > On Mon, 4 May 2020 at 06:32, Hanjun Guo <guohanjun@huawei.com> wrote: >> >> On 2020/5/2 0:10, Ard Biesheuvel wrote: >>> The ID mapping table structure of the IORT table describes the size of >>> a range using a num_ids field carrying the number of IDs in the region >>> minus one. This has been misinterpreted in the past in the parsing code, >>> and firmware is known to have shipped where this results in an ambiguity, >>> where regions that should be adjacent have an overlap of one value. >>> >>> So let's work around this by detecting this case specifically: when >>> resolving an ID translation, allow one that matches right at the end of >>> a multi-ID region to be superseded by a subsequent one. >>> >>> To prevent potential regressions on broken firmware that happened to >>> work before, only take the subsequent match into account if it occurs >>> at the start of a mapping region. >>> >>> Signed-off-by: Ard Biesheuvel <ardb@kernel.org> >>> --- >>> drivers/acpi/arm64/iort.c | 40 +++++++++++++++++--- >>> 1 file changed, 34 insertions(+), 6 deletions(-) >>> >>> diff --git a/drivers/acpi/arm64/iort.c b/drivers/acpi/arm64/iort.c >>> index 98be18266a73..9f139a94a1d3 100644 >>> --- a/drivers/acpi/arm64/iort.c >>> +++ b/drivers/acpi/arm64/iort.c >>> @@ -300,7 +300,7 @@ static acpi_status iort_match_node_callback(struct acpi_iort_node *node, >>> } >>> >>> static int iort_id_map(struct acpi_iort_id_mapping *map, u8 type, u32 rid_in, >>> - u32 *rid_out) >>> + u32 *rid_out, bool check_overlap) >>> { >>> /* Single mapping does not care for input id */ >>> if (map->flags & ACPI_IORT_ID_SINGLE_MAPPING) { >>> @@ -316,10 +316,34 @@ static int iort_id_map(struct acpi_iort_id_mapping *map, u8 type, u32 rid_in, >>> } >>> >>> if (rid_in < map->input_base || >>> - (rid_in >= map->input_base + map->id_count)) >>> + (rid_in > map->input_base + map->id_count)) >>> return -ENXIO; >>> >>> + if (check_overlap) { >>> + /* >>> + * We already found a mapping for this input ID at the end of >>> + * another region. If it coincides with the start of this >>> + * region, we assume the prior match was due to the off-by-1 >>> + * issue mentioned below, and allow it to be superseded. >>> + * Otherwise, things are *really* broken, and we just disregard >>> + * duplicate matches entirely to retain compatibility. >>> + */ >>> + pr_err(FW_BUG "[map %p] conflicting mapping for input ID 0x%x\n", >>> + map, rid_in); >> >> As we already applied a workaround here, can we add "applying >> workaround" in the error message? This will make the customers >> less uneasy to see such message in the boot log. Once the product >> was deliveried to customers, it's not that easy to update all the >> firmwares entirely. >> > > Sure. Since Will already merged this patchset, I would like to send a patch on top of it, what do you think? Thanks Hanjun
On Wed, May 06, 2020 at 08:44:55PM +0800, Hanjun Guo wrote: > On 2020/5/4 15:36, Ard Biesheuvel wrote: > > On Mon, 4 May 2020 at 06:32, Hanjun Guo <guohanjun@huawei.com> wrote: > > > On 2020/5/2 0:10, Ard Biesheuvel wrote: > > > > diff --git a/drivers/acpi/arm64/iort.c b/drivers/acpi/arm64/iort.c > > > > index 98be18266a73..9f139a94a1d3 100644 > > > > --- a/drivers/acpi/arm64/iort.c > > > > +++ b/drivers/acpi/arm64/iort.c > > > > @@ -300,7 +300,7 @@ static acpi_status iort_match_node_callback(struct acpi_iort_node *node, > > > > } > > > > > > > > static int iort_id_map(struct acpi_iort_id_mapping *map, u8 type, u32 rid_in, > > > > - u32 *rid_out) > > > > + u32 *rid_out, bool check_overlap) > > > > { > > > > /* Single mapping does not care for input id */ > > > > if (map->flags & ACPI_IORT_ID_SINGLE_MAPPING) { > > > > @@ -316,10 +316,34 @@ static int iort_id_map(struct acpi_iort_id_mapping *map, u8 type, u32 rid_in, > > > > } > > > > > > > > if (rid_in < map->input_base || > > > > - (rid_in >= map->input_base + map->id_count)) > > > > + (rid_in > map->input_base + map->id_count)) > > > > return -ENXIO; > > > > > > > > + if (check_overlap) { > > > > + /* > > > > + * We already found a mapping for this input ID at the end of > > > > + * another region. If it coincides with the start of this > > > > + * region, we assume the prior match was due to the off-by-1 > > > > + * issue mentioned below, and allow it to be superseded. > > > > + * Otherwise, things are *really* broken, and we just disregard > > > > + * duplicate matches entirely to retain compatibility. > > > > + */ > > > > + pr_err(FW_BUG "[map %p] conflicting mapping for input ID 0x%x\n", > > > > + map, rid_in); > > > > > > As we already applied a workaround here, can we add "applying > > > workaround" in the error message? This will make the customers > > > less uneasy to see such message in the boot log. Once the product > > > was deliveried to customers, it's not that easy to update all the > > > firmwares entirely. > > > > > > > Sure. > > Since Will already merged this patchset, I would like to send a patch > on top of it, what do you think? Yes, please! I figured I'd queue it, as I could always revert it if your testing came back negative but extra stuff on top is always fine. Will
On 2020/5/6 20:55, Will Deacon wrote: > On Wed, May 06, 2020 at 08:44:55PM +0800, Hanjun Guo wrote: >> On 2020/5/4 15:36, Ard Biesheuvel wrote: >>> On Mon, 4 May 2020 at 06:32, Hanjun Guo <guohanjun@huawei.com> wrote: >>>> On 2020/5/2 0:10, Ard Biesheuvel wrote: >>>>> diff --git a/drivers/acpi/arm64/iort.c b/drivers/acpi/arm64/iort.c >>>>> index 98be18266a73..9f139a94a1d3 100644 >>>>> --- a/drivers/acpi/arm64/iort.c >>>>> +++ b/drivers/acpi/arm64/iort.c >>>>> @@ -300,7 +300,7 @@ static acpi_status iort_match_node_callback(struct acpi_iort_node *node, >>>>> } >>>>> >>>>> static int iort_id_map(struct acpi_iort_id_mapping *map, u8 type, u32 rid_in, >>>>> - u32 *rid_out) >>>>> + u32 *rid_out, bool check_overlap) >>>>> { >>>>> /* Single mapping does not care for input id */ >>>>> if (map->flags & ACPI_IORT_ID_SINGLE_MAPPING) { >>>>> @@ -316,10 +316,34 @@ static int iort_id_map(struct acpi_iort_id_mapping *map, u8 type, u32 rid_in, >>>>> } >>>>> >>>>> if (rid_in < map->input_base || >>>>> - (rid_in >= map->input_base + map->id_count)) >>>>> + (rid_in > map->input_base + map->id_count)) >>>>> return -ENXIO; >>>>> >>>>> + if (check_overlap) { >>>>> + /* >>>>> + * We already found a mapping for this input ID at the end of >>>>> + * another region. If it coincides with the start of this >>>>> + * region, we assume the prior match was due to the off-by-1 >>>>> + * issue mentioned below, and allow it to be superseded. >>>>> + * Otherwise, things are *really* broken, and we just disregard >>>>> + * duplicate matches entirely to retain compatibility. >>>>> + */ >>>>> + pr_err(FW_BUG "[map %p] conflicting mapping for input ID 0x%x\n", >>>>> + map, rid_in); >>>> >>>> As we already applied a workaround here, can we add "applying >>>> workaround" in the error message? This will make the customers >>>> less uneasy to see such message in the boot log. Once the product >>>> was deliveried to customers, it's not that easy to update all the >>>> firmwares entirely. >>>> >>> >>> Sure. >> >> Since Will already merged this patchset, I would like to send a patch >> on top of it, what do you think? > > Yes, please! I figured I'd queue it, as I could always revert it if your > testing came back negative but extra stuff on top is always fine. OK, I will prepare a patch and send out for review. Thanks Hanjun
diff --git a/drivers/acpi/arm64/iort.c b/drivers/acpi/arm64/iort.c index 98be18266a73..9f139a94a1d3 100644 --- a/drivers/acpi/arm64/iort.c +++ b/drivers/acpi/arm64/iort.c @@ -300,7 +300,7 @@ static acpi_status iort_match_node_callback(struct acpi_iort_node *node, } static int iort_id_map(struct acpi_iort_id_mapping *map, u8 type, u32 rid_in, - u32 *rid_out) + u32 *rid_out, bool check_overlap) { /* Single mapping does not care for input id */ if (map->flags & ACPI_IORT_ID_SINGLE_MAPPING) { @@ -316,10 +316,34 @@ static int iort_id_map(struct acpi_iort_id_mapping *map, u8 type, u32 rid_in, } if (rid_in < map->input_base || - (rid_in >= map->input_base + map->id_count)) + (rid_in > map->input_base + map->id_count)) return -ENXIO; + if (check_overlap) { + /* + * We already found a mapping for this input ID at the end of + * another region. If it coincides with the start of this + * region, we assume the prior match was due to the off-by-1 + * issue mentioned below, and allow it to be superseded. + * Otherwise, things are *really* broken, and we just disregard + * duplicate matches entirely to retain compatibility. + */ + pr_err(FW_BUG "[map %p] conflicting mapping for input ID 0x%x\n", + map, rid_in); + if (rid_in != map->input_base) + return -ENXIO; + } + *rid_out = map->output_base + (rid_in - map->input_base); + + /* + * Due to confusion regarding the meaning of the id_count field (which + * carries the number of IDs *minus 1*), we may have to disregard this + * match if it is at the end of the range, and overlaps with the start + * of another one. + */ + if (map->id_count > 0 && rid_in == map->input_base + map->id_count) + return -EAGAIN; return 0; } @@ -404,7 +428,8 @@ static struct acpi_iort_node *iort_node_map_id(struct acpi_iort_node *node, /* Parse the ID mapping tree to find specified node type */ while (node) { struct acpi_iort_id_mapping *map; - int i, index; + int i, index, rc = 0; + u32 out_ref = 0, map_id = id; if (IORT_TYPE_MASK(node->type) & type_mask) { if (id_out) @@ -438,15 +463,18 @@ static struct acpi_iort_node *iort_node_map_id(struct acpi_iort_node *node, if (i == index) continue; - if (!iort_id_map(map, node->type, id, &id)) + rc = iort_id_map(map, node->type, map_id, &id, out_ref); + if (!rc) break; + if (rc == -EAGAIN) + out_ref = map->output_reference; } - if (i == node->mapping_count) + if (i == node->mapping_count && !out_ref) goto fail_map; node = ACPI_ADD_PTR(struct acpi_iort_node, iort_table, - map->output_reference); + rc ? out_ref : map->output_reference); } fail_map:
The ID mapping table structure of the IORT table describes the size of a range using a num_ids field carrying the number of IDs in the region minus one. This has been misinterpreted in the past in the parsing code, and firmware is known to have shipped where this results in an ambiguity, where regions that should be adjacent have an overlap of one value. So let's work around this by detecting this case specifically: when resolving an ID translation, allow one that matches right at the end of a multi-ID region to be superseded by a subsequent one. To prevent potential regressions on broken firmware that happened to work before, only take the subsequent match into account if it occurs at the start of a mapping region. Signed-off-by: Ard Biesheuvel <ardb@kernel.org> --- drivers/acpi/arm64/iort.c | 40 +++++++++++++++++--- 1 file changed, 34 insertions(+), 6 deletions(-)