Recently the I2C-controllers slave interface support was added to the
kernel I2C subsystem. In this case Linux can be used as, for example,
a I2C EEPROM machine. See [1] for details. Other than instantiating
the EEPROM-slave device from user-space there is a way to declare the
device in dts. In this case firstly the I2C bus controller must support
the slave interface. Secondly I2C-slave sub-node of that controller
must have "reg"-property with flag I2C_OWN_SLAVE_ADDRESS set (flag is
declared in [2]). That flag is declared as (1 << 30), which when set
makes dtc unhappy about too big address set for a I2C-slave:
Warning (i2c_bus_reg): /example-2/i2c@1120000/eeprom@64: I2C bus unit address format error, expected "40000064"
Warning (i2c_bus_reg): /example-2/i2c@1120000/eeprom@64:reg: I2C address must be less than 10-bits, got "0x40000064"
Similar problem would have happened if we had set the 10-bit address
flag I2C_TEN_BIT_ADDRESS in the "reg"-property.
In order to fix the problem we suggest to alter the I2C-bus reg-check
algorithm, so one would be aware of the upper bits set. Normally if no
flag specified, the 7-bit address is expected in the "reg"-property.
If I2C_TEN_BIT_ADDRESS is set, then the 10-bit address check will be
performed. The I2C_OWN_SLAVE_ADDRESS flag will be just ignored.
[1] kernel/Documentation/i2c/slave-interface.rst
[2] kernel/include/dt-bindings/i2c/i2c.h
Signed-off-by: Serge Semin <[email protected]>
Cc: Alexey Malahov <[email protected]>
Cc: Thomas Bogendoerfer <[email protected]>
Cc: Wolfram Sang <[email protected]>
Cc: Jarkko Nikula <[email protected]>
Cc: Andy Shevchenko <[email protected]>
Cc: Frank Rowand <[email protected]>
Cc: Rob Herring <[email protected]>
Cc: [email protected]
Cc: [email protected]
Cc: [email protected]
Cc: [email protected]
---
checks.c | 13 +++++++++----
1 file changed, 9 insertions(+), 4 deletions(-)
diff --git a/checks.c b/checks.c
index 4b3c486f1399..6321fc5b7404 100644
--- a/checks.c
+++ b/checks.c
@@ -1028,6 +1028,7 @@ static void check_i2c_bus_reg(struct check *c, struct dt_info *dti, struct node
const char *unitname = get_unitname(node);
char unit_addr[17];
uint32_t reg = 0;
+ uint32_t addr;
int len;
cell_t *cells = NULL;
@@ -1044,17 +1045,21 @@ static void check_i2c_bus_reg(struct check *c, struct dt_info *dti, struct node
}
reg = fdt32_to_cpu(*cells);
- snprintf(unit_addr, sizeof(unit_addr), "%x", reg);
+ addr = reg & 0x3FFFFFFFU;
+ snprintf(unit_addr, sizeof(unit_addr), "%x", addr);
if (!streq(unitname, unit_addr))
FAIL(c, dti, node, "I2C bus unit address format error, expected \"%s\"",
unit_addr);
for (len = prop->val.len; len > 0; len -= 4) {
reg = fdt32_to_cpu(*(cells++));
- if (reg > 0x3ff)
+ addr = reg & 0x3FFFFFFFU;
+ if ((reg & (1 << 31)) && addr > 0x3ff)
FAIL_PROP(c, dti, node, prop, "I2C address must be less than 10-bits, got \"0x%x\"",
- reg);
-
+ addr);
+ else if (!(reg & (1 << 31)) && addr > 0x7f)
+ FAIL_PROP(c, dti, node, prop, "I2C address must be less than 7-bits, got \"0x%x\"",
+ addr);
}
}
WARNING(i2c_bus_reg, check_i2c_bus_reg, NULL, ®_format, &i2c_bus_bridge);
--
2.26.2
On Wed, May 27, 2020 at 04:36:56PM +0300, Andy Shevchenko wrote:
> On Wed, May 27, 2020 at 03:25:25PM +0300, Serge Semin wrote:
> > Recently the I2C-controllers slave interface support was added to the
> > kernel I2C subsystem. In this case Linux can be used as, for example,
> > a I2C EEPROM machine. See [1] for details. Other than instantiating
> > the EEPROM-slave device from user-space there is a way to declare the
> > device in dts. In this case firstly the I2C bus controller must support
> > the slave interface. Secondly I2C-slave sub-node of that controller
> > must have "reg"-property with flag I2C_OWN_SLAVE_ADDRESS set (flag is
> > declared in [2]). That flag is declared as (1 << 30), which when set
> > makes dtc unhappy about too big address set for a I2C-slave:
> >
> > Warning (i2c_bus_reg): /example-2/i2c@1120000/eeprom@64: I2C bus unit address format error, expected "40000064"
> > Warning (i2c_bus_reg): /example-2/i2c@1120000/eeprom@64:reg: I2C address must be less than 10-bits, got "0x40000064"
> >
> > Similar problem would have happened if we had set the 10-bit address
> > flag I2C_TEN_BIT_ADDRESS in the "reg"-property.
> >
> > In order to fix the problem we suggest to alter the I2C-bus reg-check
> > algorithm, so one would be aware of the upper bits set. Normally if no
> > flag specified, the 7-bit address is expected in the "reg"-property.
> > If I2C_TEN_BIT_ADDRESS is set, then the 10-bit address check will be
> > performed. The I2C_OWN_SLAVE_ADDRESS flag will be just ignored.
> >
> > [1] kernel/Documentation/i2c/slave-interface.rst
> > [2] kernel/include/dt-bindings/i2c/i2c.h
>
> ...
>
> > + addr = reg & 0x3FFFFFFFU;
> > + if ((reg & (1 << 31)) && addr > 0x3ff)
> > FAIL_PROP(c, dti, node, prop, "I2C address must be less than 10-bits, got \"0x%x\"",
> > - reg);
> > -
> > + addr);
> > + else if (!(reg & (1 << 31)) && addr > 0x7f)
> > + FAIL_PROP(c, dti, node, prop, "I2C address must be less than 7-bits, got \"0x%x\"",
> > + addr);
>
> 1 << 31 is UB.
Good point. Thanks.
-Sergey
>
> --
> With Best Regards,
> Andy Shevchenko
>
>
On Wed, May 27, 2020 at 03:25:25PM +0300, Serge Semin wrote:
> Recently the I2C-controllers slave interface support was added to the
> kernel I2C subsystem. In this case Linux can be used as, for example,
> a I2C EEPROM machine. See [1] for details. Other than instantiating
> the EEPROM-slave device from user-space there is a way to declare the
> device in dts. In this case firstly the I2C bus controller must support
> the slave interface. Secondly I2C-slave sub-node of that controller
> must have "reg"-property with flag I2C_OWN_SLAVE_ADDRESS set (flag is
> declared in [2]). That flag is declared as (1 << 30), which when set
> makes dtc unhappy about too big address set for a I2C-slave:
>
> Warning (i2c_bus_reg): /example-2/i2c@1120000/eeprom@64: I2C bus unit address format error, expected "40000064"
> Warning (i2c_bus_reg): /example-2/i2c@1120000/eeprom@64:reg: I2C address must be less than 10-bits, got "0x40000064"
>
> Similar problem would have happened if we had set the 10-bit address
> flag I2C_TEN_BIT_ADDRESS in the "reg"-property.
>
> In order to fix the problem we suggest to alter the I2C-bus reg-check
> algorithm, so one would be aware of the upper bits set. Normally if no
> flag specified, the 7-bit address is expected in the "reg"-property.
> If I2C_TEN_BIT_ADDRESS is set, then the 10-bit address check will be
> performed. The I2C_OWN_SLAVE_ADDRESS flag will be just ignored.
>
> [1] kernel/Documentation/i2c/slave-interface.rst
> [2] kernel/include/dt-bindings/i2c/i2c.h
...
> + addr = reg & 0x3FFFFFFFU;
> + if ((reg & (1 << 31)) && addr > 0x3ff)
> FAIL_PROP(c, dti, node, prop, "I2C address must be less than 10-bits, got \"0x%x\"",
> - reg);
> -
> + addr);
> + else if (!(reg & (1 << 31)) && addr > 0x7f)
> + FAIL_PROP(c, dti, node, prop, "I2C address must be less than 7-bits, got \"0x%x\"",
> + addr);
1 << 31 is UB.
--
With Best Regards,
Andy Shevchenko
Recently the I2C-controllers slave interface support was added to the
kernel I2C subsystem. In this case Linux can be used as, for example,
a I2C EEPROM machine. See [1] for details. Other than instantiating
the EEPROM-slave device from user-space there is a way to declare the
device in dts. In this case firstly the I2C bus controller must support
the slave interface. Secondly I2C-slave sub-node of that controller
must have "reg"-property with flag I2C_OWN_SLAVE_ADDRESS set (flag is
declared in [2]). That flag is declared as (1 << 30), which when set
makes dtc unhappy about too big address set for a I2C-slave:
Warning (i2c_bus_reg): /example-2/i2c@1120000/eeprom@64: I2C bus unit address format error, expected "40000064"
Warning (i2c_bus_reg): /example-2/i2c@1120000/eeprom@64:reg: I2C address must be less than 10-bits, got "0x40000064"
Similar problem would have happened if we had set the 10-bit address
flag I2C_TEN_BIT_ADDRESS in the "reg"-property.
In order to fix the problem we suggest to alter the I2C-bus reg-check
algorithm, so one would be aware of the upper bits set. Normally if no
flag specified, the 7-bit address is expected in the "reg"-property.
If I2C_TEN_BIT_ADDRESS is set, then the 10-bit address check will be
performed. The I2C_OWN_SLAVE_ADDRESS flag will be just ignored.
[1] kernel/Documentation/i2c/slave-interface.rst
[2] kernel/include/dt-bindings/i2c/i2c.h
Signed-off-by: Serge Semin <[email protected]>
Cc: Alexey Malahov <[email protected]>
Cc: Thomas Bogendoerfer <[email protected]>
Cc: Wolfram Sang <[email protected]>
Cc: Jarkko Nikula <[email protected]>
Cc: Andy Shevchenko <[email protected]>
Cc: Frank Rowand <[email protected]>
Cc: Rob Herring <[email protected]>
Cc: [email protected]
Cc: [email protected]
Cc: [email protected]
Cc: [email protected]
---
Changelog v2:
- Use unsigned numeric literal in the left-shit operation to avoid UB.
---
checks.c | 13 +++++++++----
1 file changed, 9 insertions(+), 4 deletions(-)
diff --git a/checks.c b/checks.c
index 4b3c486f1399..7091d1bc38d2 100644
--- a/checks.c
+++ b/checks.c
@@ -1028,6 +1028,7 @@ static void check_i2c_bus_reg(struct check *c, struct dt_info *dti, struct node
const char *unitname = get_unitname(node);
char unit_addr[17];
uint32_t reg = 0;
+ uint32_t addr;
int len;
cell_t *cells = NULL;
@@ -1044,17 +1045,21 @@ static void check_i2c_bus_reg(struct check *c, struct dt_info *dti, struct node
}
reg = fdt32_to_cpu(*cells);
- snprintf(unit_addr, sizeof(unit_addr), "%x", reg);
+ addr = reg & 0x3FFFFFFFU;
+ snprintf(unit_addr, sizeof(unit_addr), "%x", addr);
if (!streq(unitname, unit_addr))
FAIL(c, dti, node, "I2C bus unit address format error, expected \"%s\"",
unit_addr);
for (len = prop->val.len; len > 0; len -= 4) {
reg = fdt32_to_cpu(*(cells++));
- if (reg > 0x3ff)
+ addr = reg & 0x3FFFFFFFU;
+ if ((reg & (1U << 31)) && addr > 0x3ff)
FAIL_PROP(c, dti, node, prop, "I2C address must be less than 10-bits, got \"0x%x\"",
- reg);
-
+ addr);
+ else if (!(reg & (1U << 31)) && addr > 0x7f)
+ FAIL_PROP(c, dti, node, prop, "I2C address must be less than 7-bits, got \"0x%x\"",
+ addr);
}
}
WARNING(i2c_bus_reg, check_i2c_bus_reg, NULL, ®_format, &i2c_bus_bridge);
--
2.26.2
> + addr = reg & 0x3FFFFFFFU;
> + snprintf(unit_addr, sizeof(unit_addr), "%x", addr);
Hmm, this hardcoded value will not work if we ever need to add another
bit. I hope this will never happen, though.
> + if ((reg & (1U << 31)) && addr > 0x3ff)
Same here with bit 31. I haven't checked DTC but can't we import the
header with the defines into the project? Or is this then a circular
dependency?
On Sat, May 30, 2020 at 11:31:52AM +0200, Wolfram Sang wrote:
>
> > + addr = reg & 0x3FFFFFFFU;
> > + snprintf(unit_addr, sizeof(unit_addr), "%x", addr);
>
> Hmm, this hardcoded value will not work if we ever need to add another
> bit. I hope this will never happen, though.
>
> > + if ((reg & (1U << 31)) && addr > 0x3ff)
>
> Same here with bit 31.
I'd be glad to use a macro or some helper here, but alas there is no
ready-to-use i2c-related one in the dtc code. See, there are hard-coded
literals in the PCI nodes checkers (check_pci_device_reg(),
check_pci_device_bus_num()) and the hard-coded literals've been in the
i2c-nodes checkers even before this patch.
> I haven't checked DTC but can't we import the
> header with the defines into the project? Or is this then a circular
> dependency?
>
I guess importing header would be much better than the hard-coded values
currently used. What do the code maintainers say about that? Any idea how it
is supposed to be implemented?
-Sergey
On Sat, May 30, 2020 at 3:32 AM Wolfram Sang <[email protected]> wrote:
>
>
> > + addr = reg & 0x3FFFFFFFU;
> > + snprintf(unit_addr, sizeof(unit_addr), "%x", addr);
>
> Hmm, this hardcoded value will not work if we ever need to add another
> bit. I hope this will never happen, though.
I had this concern and requested the first time this was submitted
(and abandoned) to just mask out the top byte. However, Joel's version
of this fix[1] does some actual checks on 10-bit addressing, so I've
dropped that request.
> > + if ((reg & (1U << 31)) && addr > 0x3ff)
>
> Same here with bit 31. I haven't checked DTC but can't we import the
> header with the defines into the project? Or is this then a circular
> dependency?
Easier to just duplicate the define here which Joel's patches do.
Rob
[1] https://www.spinics.net/lists/devicetree-compiler/msg03196.html
> Easier to just duplicate the define here which Joel's patches do.
Well, seems this case is closed then. Thanks everyone!