Skip to content
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

Merged
merged 3 commits into from
Nov 20, 2023

Conversation

p-woj
Copy link
Contributor

@p-woj p-woj commented Nov 16, 2023

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

@fkokosinski
Copy link
Member

Assigned myself because this is a RISC-V related change.

@fkokosinski
Copy link
Member

@ycsin - could you take a look? It seems that it reverts some changes that you've introduced,

ycsin
ycsin previously approved these changes Nov 16, 2023
Copy link
Collaborator

@ycsin ycsin left a 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

@ycsin ycsin added bug The issue is a bug, or the PR is fixing a bug Regression Something, which was working, does not anymore labels Nov 16, 2023
@ycsin ycsin dismissed their stale review November 16, 2023 12:28

had another look

Copy link
Collaborator

@ycsin ycsin left a 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?

@p-woj
Copy link
Contributor Author

p-woj commented Nov 16, 2023

Thanks for the suggestion. I also adapted existing uses of the same pattern in plic_init and riscv_plic_set_priority.

@@ -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);
Copy link
Member

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.

Copy link
Contributor Author

@p-woj p-woj Nov 16, 2023

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.

Copy link
Member

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.

ycsin
ycsin previously approved these changes Nov 16, 2023
Copy link
Collaborator

@ycsin ycsin left a 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?

@p-woj
Copy link
Contributor Author

p-woj commented Nov 16, 2023

Thanks for the suggestions - I fixed the naming of these functions and also changed "offset" to "index" in reg_offset for clarity.

@cfriedt
Copy link
Member

cfriedt commented Nov 16, 2023

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;

@cfriedt
Copy link
Member

cfriedt commented Nov 16, 2023

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?

@p-woj - would also be good to change get_claim_complete_addr() and get_threshold_priority_offset() to return type mem_addr_t so that the type assignment is consistent.

Honestly, not 100% sure about the utility of using mem_addr_t (i.e. uintptr_t) at all in this context, and we shouldn't be defining new types unnecessarily. We should use a pointer type when referring to a memory-mapped location in the address space.

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>
@p-woj
Copy link
Contributor Author

p-woj commented Nov 16, 2023

Thanks, that's a much better approach. Now has the types fixed as well

Copy link
Member

@cfriedt cfriedt left a 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.

@cfriedt
Copy link
Member

cfriedt commented Nov 16, 2023

@ycsin @p-woj - just for our own peace of mind - is this actually getting run through some tests? If not, we should add one in this PR as a second commit.

@p-woj
Copy link
Contributor Author

p-woj commented Nov 16, 2023

I don't think it is currently covered by any tests.

We need this small patch to enable running ROBOT_FILES=shell_module.robot west build -p -b mpfs_icicle -s samples/subsys/shell/shell_module -t run_renode_test to test this regression. IIUC to have Twister run it automatically we'd have to enable it as a default platform for tests, so I think this might be something for a separate PR.

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)

mpfs_icicle is used here because it's one of the platforms where we originally found this regression and it already has emulation support.

@cfriedt
Copy link
Member

cfriedt commented Nov 16, 2023

I think this might be something for a separate PR.

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?

Copy link
Member

@cfriedt cfriedt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needs test / issue

@cfriedt
Copy link
Member

cfriedt commented Nov 17, 2023

One way to easily test this (without relying on a particular hardware configuration) is to simply do some whitebox testing.

I.e. in intc_plic.c, do something like this:

#ifdef CONFIG_TEST_INTC_PLIC
#define INTC_PLIC_STATIC
#else
#define INTC_PLIC_STATIC static
#endif

In tests/drivers/build_all/interrupt_controller/intc_plic/Kconfig add something like

config TEST_INTC_PLIC
	bool
	default y
	help
	   Declare some intc_plic.c functions in the global scope for verification.
  • Remove the definition of main() from tests/drivers/build_all/interrupt_controller/intc_plic/src/main.c
  • Create a proper ZTest testsuite
  • Re-declare the necessary intc_plic.c function prototypes locally
  • Remove build_only: true from testcase.yaml

@p-woj
Copy link
Contributor Author

p-woj commented Nov 17, 2023

I kept the existing build-only tests as they were, since the build_all directory has only build_only tests, and added a separate ZTest suite which is very whitebox - it tests local_irq_to_reg_index and local_irq_to_reg_offset directly. Please take a look.

Add a test to verify that the regression from
ffb8f31 is fixed.

Signed-off-by: Piotr Wojnarowski <pwojnarowski@antmicro.com>
@cfriedt
Copy link
Member

cfriedt commented Nov 17, 2023

I kept the existing build-only tests as they were, since the build_all directory has only build_only tests, and added a separate ZTest suite which is very whitebox - it tests local_irq_to_reg_index and local_irq_to_reg_offset directly. Please take a look.

I will defer to @ycsin's review

Copy link
Collaborator

@ycsin ycsin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@carlescufi carlescufi merged commit 6670dbe into zephyrproject-rtos:main Nov 20, 2023
18 checks passed
@p-woj p-woj deleted the plic_width branch November 20, 2023 10:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area: Interrupt Controller area: RISCV RISCV Architecture (32-bit & 64-bit) bug The issue is a bug, or the PR is fixing a bug Regression Something, which was working, does not anymore
Projects
None yet
Development

Successfully merging this pull request may close these issues.

PLIC interrupt configuration doesn't work for interrupt numbers above 0x1f
7 participants