-
Notifications
You must be signed in to change notification settings - Fork 8.2k
Add API for flash write protection and readout protection functionality #53441
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
Add API for flash write protection and readout protection functionality #53441
Conversation
|
Added RFC #53905 |
a7355d9 to
35a5b52
Compare
|
Replaced flash API functions for WP, RDP with one "extended operations" function as discussed in #53905 |
include/zephyr/drivers/flash.h
Outdated
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 am not yet convinced about the function parameters. Would it be clearer to pass instead:
device *dev, uint32_t code, const void *code_param
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'm fine with one pointer instead of two. One note: we will need to have separate codes to get and set option (which is also fine). I would remove also const (so, const void *code_param will become void *code_data), because we need to pass data from get codes to the user.
Do you strongly prefer void * over uintptr_t *?
I will wait for comments from @de-nordic before changing this.
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'm fine with one pointer instead of two. One note: we will need to have separate codes to
getandsetoption (which is also fine). I would remove alsoconst(so,const void *code_paramwill becomevoid *code_data), because we need to pass data fromgetcodes to the user.
I would pass the code_data pointer as a const void *, if it would be a "get" operation it would contain a pointer to where the data can be written (not a const) and a size that has been allocated for that. In a "set" operation it is also possible to pass the size. So the idea was not to use this directly but only after a cast to something containing a size and a buffer location.
Do you strongly prefer
void *overuintptr_t *?
void * is used more than uintptr_t * in the code and make it more clear that it is expected to be casted.
I will wait for comments from @de-nordic before changing this.
OK
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.
So the idea was not to use this directly but only after a cast to something containing a size and a buffer location.
I'm not sure if this is better than passing pointer and size in a flash_ex_op call. From the user perspective, in order to get a write protect status I need to allocate two structures: The first for a pointer and size, the second for the results. For me, this sounds like an overkill, but maybe I'm missing something.
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.
It is more general and versatile, suppose that for some operation you would need 2 buffers each with their own size, this wouldn't fit in the pointer + size setup. As a bonus this also only needs 1 extra api function and not 2.
Maybe it would be better to use "context" (op_ctx) instead of code_param.
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 agree that the difference is small, but I don't see a reason to provide the *out. IMHO this is better replaced by a sizeof the struct that is passed to avoid casting errors/reading outside struct boundaries.
Size does not matter. The driver should only understand predefined structures as op context, there is no point to size them. If we provide size, how do we use it? Do we compare it against known sizes and drop unsupported? Does not seem to practical, it is rather one time error. Do we allow any size? What if user mismatches size between structures? Does giving size saves us from anything? We will just overwrite or read pass what we should, and this is in system mode, so we will not figure that out prior to returning to user mode anyway.
OK to drop the sizeof requirement,
Probably we would also need to define some "standard" struct layouts (in flash.h) to be passed to this function:
1. Structure for a simple command passed, 2. Structure for a read type command passed, 3. Structure for a write type command passed, 4. Structure for a read+write type command passed (with 2 buffers),We already have API call for most of above cases so we know how they behave, and they are unique themselves and they already do not cover what we are trying to do, they cover what we had already done; the purpose of this API call, and the vendor range itself, is so that we could evaluate things we do not know yet by observing how users try to overcome problems in real scenarios. Then when we collect enough data, or what kind of problems users have how do they prefer to use API, how certain functionalities are provided by different vendors and then design common op code, for these functionalities, in system code space.
You might have overlooked the "type" that I inserted before "command ..." (and forgotten for the "simple" case 😀 ):
a. the get protection would be a "read type command structure",
b. the set protection would be a "write type command structure",
If these structures are not defined how would they be included by the user ? Would users have to include a vendor specific include file ? Where should these include files be placed ?
As illustrated by your examples I think all should be possible with the chosen function definition. I still don't see the benefit of the *out because a context needs to be provided anyhow and I don't like the obligation to provide NULL if *out is not needed. But you can decide.
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.
You might have overlooked the "type" that I inserted before "command ..." (and forgotten for the "simple" case 😀 ): a. the get protection would be a "read type command structure", b. the set protection would be a "write type command structure",
I might have, I would be a really bad lawyer.
If these structures are not defined how would they be included by the user ? Would users have to include a vendor specific include file ? Where should these include files be placed ?
Yes. That is the idea #53905 (comment). When we standardize feature with system wide code, than user can switch to using it.
Non standard codes would not likely to be used in Zephyr code base, as they would start favoring some selected platform.
In case of the RD/WR locks on flash this is actually thing that user will know what they are doing and usage would be app specific, rather then something done in system; but when we finally have some common code and structures derived, after gaining experience with various platforms and mem chips, we could try to evaluate using this to set some app protections, by for example MCUboot, to prevent accidental writes over running application.
I am assemblysaurus from the argsonstack coding period, so passing unused params on stack before doing syscall/int is stuff I can still accept. I remember, though, one of my teachers mentioning to avoid designing more than five param functions to avoid unneeded stack ops, and because x86 didn't have that many registers back then.
Anyway gathering pros and cons here.
Pros/cons for the flash_ex_op(..., code, const in, out)
- + No need for copy in
inwhen onlyoutpointer is needed; - + in can be used to pass integer, for simple operations that do not return anything (like set power level);
- - Need to save-pass-restore register used by
outand set it to NULL when not used; - +
outcan be used to either pass pointer to for object of predefined size, or pointer to structure that will define output and, being non-const, could be filled in in place by function;
Pros/cons for the flash_ex_op(..., in_out)
- - No need to copy in
in_out`` to getoutpointer, unless we agree to makein_out` non-const, which I would rather not do (vs +on 1 in above list); - same as 2 in above;
- + No need to save-pass-restore
outregister (vs - on 3 in above list, flash saved) - - if
in_outis const and can not be written directly, just for being inconvenient, otherwise thein_outmay point to some additional structure;
To explain more what I mean by 4:
struct something_in {
uint32_t in_size;
uint8_t *in_buf;
uint32_t out_offset; // Offset where to write not within out buffer;
};
struct something_out {
uint32_t out_size;
uint8_t *out_buf;
};
struct something_in sin = { .. some init };
struct something_out sout = { .out_size = max_out_buf_size, .out_buf = out_buf_ptr };
// When successful, the &sout will have .out_size replaced with size of data written to out_buf_ptr; the function can not directly modify the sin but can do that with sout;
ret = flash_ex_op(dev, code, &sin, &sout);
// Now we have function that does not use extra input info, and can still read/write the out.
ret = flash_ex_op(dev, code, NULL, &sout);
For me using separate in and out is more convenient, at the cost of passing NULL sometimes and one more set of store-pass-restore operations at call. It also allows to easily use the in (since the intptr_t) as basic integer parameter, for which you do not have to define extra input structure.
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 would still rather settle on flash_ex_op(struct device *device, uint32_t code, const void *code_ctx) because:
a. It is similar to disk io_ctl,
b. In the case a size passed for *out, there would be a deviation from first passing the pointer and then the length,
c. Even in the case a size is passed for *out probably another parameter e.g. a offset will need to be passed as well, so there would still be the need to setup a context,
d. The special use case where it is to be expected that only one value will be passed for power level will probably need to be standardized into a new (generic) api function,
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.
To make sure that I'm on the same page: by "copy in" and "copy out" you mean accordingly z_user_from_copy() and z_user_to_copy() used in a verifier as described in https://docs.zephyrproject.org/latest/kernel/usermode/syscalls.html#verification-memory-access-policies? Copy function requires size of structure it's going to copy, but this depends on the extended operation code, so the verifier needs to be implemented in the driver (similar to subsys/net/ip/net_if.c) instead of some dedicated file, is this correct?
The link above mentions that we need to make a copy to protect against situation in which userspace changes structure passed to kernel, after it was verified. Does it mean that we don't need to copy in structures that are const?
No need to copy in
in_outto getoutpointer, unless we agree to makein_outnon-const, which I would rather not do (vs +on 1 in above list);
I'm not sure if I understand this correctly. In this case we actually need to copy in in order to get out pointer, don't we? Or we can merge input and output parameters in one structure (but it needs to be non-const).
It is similar to disk
io_ctl,
flash_ex_op(struct device *device, uint32_t code, const void *code_ctx)
mmc_ioctl(struct sd_card *card, uint8_t cmd, void *buf)
The difference is that ioctl doesn't require the buffer to be const. This one keyword affects implementation, so IMO they are not similar.
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.
It seems that the syscall interface will require the standardization of the "context" passed to the flash_ex_op call. Regarding the difference with mmc_ioctl call, it is indeed different, but I don't think the mmc_ioctl call can be made in accordance with the syscall interface (regardless of wether it is a const or not it should pass a size).
I am thinking about a generic way that would allow passing the required info, but I haven't found one yet (even when splitting it up in a get and set method) taking into account that the parameter count should not be more than 4.
35a5b52 to
eee3246
Compare
|
@semihalf-duda-patryk, @de-nordic, in order to get this api in line with the syscall requirements the following should work: So the interface would look like: This somewhat limits the possibilities, but even the most complicated function I can think of should be possible with a combination of a set+get command. And it keeps the number of function parameters limited to 4. What is your opinion? |
What is the purpose of |
|
In many cases the An example I could think of: suppose you would need to set the protection at a certain address by providing a special code (only known to the user, not the driver), the offset could be used to provide the address, while the data is used to provide the code. This could of course be combined into the |
It's sounds like |
Only when it is exposed by the driver. Regarding the special code I was thinking about security unlock data (e.g. a password or passphrase). |
eee3246 to
da1a9ff
Compare
|
Added |
Yeah, I don't like this to much, pushing all vendor iocodes into |
Yup. I agree that putting vendor codes to I wonder, what if we had a two different flash-controllers (e.g. one internal and one external)? We will need to check which vendor verification function we need to call. @de-nordic what do you think about it? |
|
@semihalf-duda-patryk, if we could standardize the interface that allows syscall to do its functionality there would be no need for |
We are trying to add a syscall which will be used for many purposes. Our interface will be common denominator of all functionalities we are trying to expose with it - some code to describe operation and buffer used by it. https://docs.zephyrproject.org/latest/kernel/usermode/syscalls.html#verification-memory-access-policies mentions that: It means that we have to make a copy of provided buffer in verification function. To do that without vendor specific structures definitions, we need to provide size of a buffer while calling syscall. Another thing is that: If we want to avoid interpreting provided buffer in verification function we can't pass any pointers inside the structure. |
adc3b96 to
e3b6cf7
Compare
e3b6cf7 to
e81d849
Compare
|
@de-nordic Is there anything else I should fix? |
de-nordic
left a comment
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.
Just add one more commit with release notes update, informing users on addition of the API call and Kconfigs.
|
@de-nordic Sure. Do you have some example commit with release note update? |
Besides of standard flash operations like write or erase, flash controllers also support additional features like write protection or readout protection. These features are not available in every flash controller, what's more controllers can implement it in a different way. It doesn't make sense to add a separate flash API function for every flash controller feature, because it could be unique (supported on small number of flash controllers) or the API won't be able to represent the same feature on every flash controller. Extended operation interface provides flexible way for supporting flash controller features. Code space is divided equally into Zephyr codes (MSb == 0) and vendor codes (MSb == 1). This way we can easily add extended operations to the drivers without cluttering the API or problems with API incompatibility. Extended operation can be promoted from vendor codes to Zephyr codes if the feature is available in most flash controllers and can be represented in the same way. Signed-off-by: Patryk Duda <[email protected]>
Something like 516b673 but in Flash section of drivers. Btw. Catch me on discord under de-nordic, if you need to discuss this with lower latency. |
Add information about Flash API changes. Signed-off-by: Patryk Duda <[email protected]>
e81d849 to
f5045e4
Compare
This PR extends flash API to expose flash write protection (persistent across reboots) and readout protection (temporary or permanent) functionality.
The flash API was extended by functions responsible for checking and changing WP and RDP state. Enabling RDP permanently is guarded by FLASH_ALLOW_PERMANENT_READOUT_PROTECTION option. Disabling RDP (which triggers mass erase) is guarded by FLASH_ALLOW_DISABLING_READOUT_PROTECTION.
Best regards,
Patryk