-
Notifications
You must be signed in to change notification settings - Fork 6.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
drivers: intc: plic: Fix memory-mapped register offset calculation #65283
Conversation
Assigned myself because this is a RISC-V related change. |
@ycsin - could you take a look? It seems that it reverts some changes that you've introduced, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch! Thanks for the fix
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I had another look and thought that this fix changes the original intention of the function to calculate the index
What do you think if we have another function to do the conversion, i.e.:
static inline uint32_t offset_to_mem_addr(uint32_t offset)
{
return offset * sizeof(uint32_t);
}
and apply that to every mem_addr_t
arithmetics as necessary?
Thanks for the suggestion. I also adapted existing uses of the same pattern in |
@@ -115,7 +120,8 @@ static inline const struct device *get_plic_dev_from_irq(uint32_t irq) | |||
static int riscv_plic_is_edge_irq(const struct device *dev, uint32_t local_irq) | |||
{ | |||
const struct plic_config *config = dev->config; | |||
mem_addr_t trig_addr = config->trig + local_irq_to_reg_offset(local_irq); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't we just fix local_irq_to_reg_offset()
in this PR? Offset is in bytes, so we're basically leaving a land mine for whoever hits this problem next time.
Also, kind of wondering how that even passed CI... we have tests, right? Is it a build-only test? Would maybe explain how local_irq_to_reg_offset()
was somehow skipped.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think what @ycsin meant is that reg_offset
is the offset expressed in the number of registers.
As for tests, I'm not sure if these run in CI, but on the QEMU virt platforms the uart (the only device connected to the plic) interrupt number is small enough such that the calculated offset was (correctly) 0, so the lacking multiplication was of course irrelevant.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe a misnomer - reg_index would be better in the case.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you dont mind, could you please fix the naming of get_claim_complete_offset
& get_threshold_priority_offset
to _addr
as well in this PR (as another commit) so that things will be consistent in the driver?
Thanks for the suggestions - I fixed the naming of these functions and also changed "offset" to "index" in |
Just (another) suggestion diff --git a/drivers/interrupt_controller/intc_plic.c b/drivers/interrupt_controller/intc_plic.c
index 41ee75823b..0fe7b902d0 100644
--- a/drivers/interrupt_controller/intc_plic.c
+++ b/drivers/interrupt_controller/intc_plic.c
@@ -59,11 +59,16 @@ struct plic_config {
static uint32_t save_irq;
static const struct device *save_dev;
-static inline uint32_t local_irq_to_reg_offset(uint32_t local_irq)
+static inline uint32_t local_irq_to_reg_index(uint32_t local_irq)
{
return local_irq >> LOG2(PLIC_REG_SIZE);
}
+static inline uint32_t local_irq_to_reg_offset(uint32_t local_irq)
+{
+ return local_irq_to_reg_index(local_irq) * sizeof(uint32_t);
+}
+
static inline uint32_t get_plic_enabled_size(const struct device *dev)
{
const struct plic_config *config = dev->config; |
@p-woj - would also be good to change Honestly, not 100% sure about the utility of using Using integer types when referring to memory locations really only makes sense in a limited number of circumstances (e.g. referring to a location outside of the memory-mapped address space). Here, I think it's being used to circumvent having to deal with pointer arithmetic (which was a mistake we have just paid for). @fkokosinski - what are your thoughts there? |
`get_claim_complete_offset` and `get_threshold_priority_offset` actually return addresses directly. Rename them to `_addr` for consistency within the driver. Also change their return type to `mem_addr_t`. Signed-off-by: Piotr Wojnarowski <pwojnarowski@antmicro.com>
Previously, the PLIC's registers were accessed through uint32_t *, so all calculated offsets were effectively multiplied by sizeof(uint32_t). Do the same manuallly now that we have mem_addr_t/sys_read32. Signed-off-by: Piotr Wojnarowski <pwojnarowski@antmicro.com>
Thanks, that's a much better approach. Now has the types fixed as well |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Works for me - It looks like mem_addr_t
is baked in already way up the stack, so save that for another time.
I don't think it is currently covered by any tests. We need this small patch to enable running diff --git a/boards/riscv/mpfs_icicle/board.cmake b/boards/riscv/mpfs_icicle/board.cmake
index 0fed4848fa..1f3346da5a 100644
--- a/boards/riscv/mpfs_icicle/board.cmake
+++ b/boards/riscv/mpfs_icicle/board.cmake
@@ -2,3 +2,4 @@
set(SUPPORTED_EMU_PLATFORMS renode)
set(RENODE_SCRIPT ${CMAKE_CURRENT_LIST_DIR}/support/mpfs250t.resc)
+set(RENODE_UART sysbus.mmuart0)
|
We definitely want to have the test that verifies the fix in the same PR as the fix. You will need to create an issue as well (if one doesn't already exist), since it's a regression. Not sure if this needs to be backported. @ycsin - can you suggest a way to test this on a possibly alternate platform? Does the fix need to be backported to v3.5-branch? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Needs test / issue
One way to easily test this (without relying on a particular hardware configuration) is to simply do some whitebox testing. I.e. in #ifdef CONFIG_TEST_INTC_PLIC
#define INTC_PLIC_STATIC
#else
#define INTC_PLIC_STATIC static
#endif In
|
I kept the existing build-only tests as they were, since the |
Add a test to verify that the regression from ffb8f31 is fixed. Signed-off-by: Piotr Wojnarowski <pwojnarowski@antmicro.com>
I will defer to @ycsin's review |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
This MR fixes register offset calculation in the PLIC driver.
ffb8f31 removed a pointer-arithmetic-induced multiplication of the calculated register offset. As a result the register offsets are 4 times too small. For example, riscv_plic_irq_enable(10251) with 2nd level interrupts enabled and the default 8-bit size currently writes to offset 0x1 instead of 0x4.
Only interrupt numbers above 0x1f are affected.
Fixes: #65321