Pointer type write only

I am writing software for an embedded system.

We use pointers to access FPGA device registers.
Some registers are read-only, while others are write-only.

Write-only registers will return undefined values ​​when read.

I want to determine the type of pointer that will allow the compiler to detect when reading values ​​from registers for write only (dereferencing aka).

Is it possible to create a write-only pointer using only the C language syntax?
(We are developing the first prototype using C, but we are switching to C ++ on the 2nd generation.)

How to create an efficient write-only pointer in C ++? (Remember that this is not tracking elements in dynamic memory, but accessing hardware addresses.)

This code is used in an embedded system where safety and quality are the most important.

+52
c ++ c pointers readonly
Apr 17 '13 at 0:12
source share
5 answers

I would probably write for each a tiny wrapper class:

template <class T> class read_only { T volatile *addr; public: read_only(int address) : addr((T *)address) {} operator T() volatile const { return *addr; } }; template <class T> class write_only { T volatile *addr; public: write_only(int address) : addr ((T *)address) {} // chaining not allowed since it write only. void operator=(T const &t) volatile { *addr = t; } }; 

At least assuming your system has a reasonable compiler, I would expect both of these functions to be optimized so that the generated code is indistinguishable from using a raw pointer. Using:

 read_only<unsigned char> x(0x1234); write_only<unsigned char> y(0x1235); y = x + 1; // No problem x = y; // won't compile 
+61
Apr 17 '13 at 0:31
source share
— -

I would use a combination of structures to create a register and a couple of functions to handle them.

In fpga_register.h you have something like

 #define FPGA_READ = 1; #define FPGA_WRITE = 2; typedef struct register_t { char permissions; } FPGARegister; FPGARegister* fpga_init(void* address, char permissions); int fpga_write(FPGARegister* register, void* value); int fpga_read(FPGARegister* register, void* value); 

using READ and WRITE in xor to express permissions.

Than in fpga_register.c you have to define a new structure

 typedef struct register_t2 { char permissions; void * address; } FPGARegisterReal; 

so that you return a pointer to it instead of a pointer to FPGARegister on fpga_init .

Then on fpga_read and fpga_write you check permissions and

  • if the operation is allowed, drop FPGARegister from the argument in FPGARegisterReal , perform the required action (set or read the value) and return the success code
  • If the operation is not allowed, just return the error code

Thus, no one, including the header file, will be able to access the FPGARegisterReal structure and, therefore, will not have direct access to the register address. Obviously, you could hack it, but I am absolutely sure that such deliberate hacks are not your real problems.

+8
Apr 17 '13 at 0:56
source share

I worked with a lot of hardware, and some of them have read-only or write-only registers (or different functions depending on whether you read or write to the register, which makes fun when someone decides to do "reg | = 4;" instead of remembering the value that it should have, set bit 2 and write a new value, just like you. Nothing like trying to debug hardware that has random bits appearing and disappearing from registers , you can’t read!;) I still don’t see actual blocking attempts to read from the register with the recording or writing to read-only registers.

By the way, I said that having registers that are “write-only” is a REALLY bad idea because you cannot read to check if the software is installed correctly, which makes debugging very difficult - and people writing drivers, They don’t like to debug complex problems that can be done very easily with two lines of VHDL or Verilog code.

If you have some control over the layout of the register, I would advise you to enter read-only registers with an address aligned with 4 KB, and writeonly registers are registered at another address with a 4 KB address [more than 4 KB in order]. You can then program the memory controller on the equipment to prevent access.

Or let the hardware interrupt if registers that should not be read are read, or registers that should not be written are written. I assume hardware interrupts for other purposes?

Other suggestions made using various solutions in C ++ are great, but in reality this does not stop those who intend to use registers directly, so if this is really a security problem (and not "let it make it inconvenient"), then you should be hardware to protect against misuse of hardware.

+8
Apr 17 '13 at 1:11
source share

In C, you can use pointers for incomplete types to prevent all dereferences:




 /* writeonly.h */ typedef struct writeonly *wo_ptr_t; 



 /* writeonly.c */ #include "writeonly.h" struct writeonly { int value }; /*...*/ FOO_REGISTER->value = 42; 



 /* someother.c */ #include "writeonly.h" /*...*/ int x = FOO_REGISTER->value; /* error: deref'ing pointer to incomplete type */ 



Only writeonly.c , or any code with a struct writeonly , can dereference a pointer. This code, of course, can also accidentally read the value, but at least any other code does not allow to dereference pointers together, being able to pass these pointers and store them in variables, arrays and structures.

writeonly.[ch] can provide a function to write the value.

+7
Apr 17 '13 at 5:46
source share

I don’t see an elegant way to do this in C. I still see a way to do this:

 #define DEREF_PTR(type, ptr) type ptr; \ typedef char ptr ## _DEREF_PTR; #define NO_DEREF_PTR(type, ptr) type ptr; \ #define DEREFERENCE(ptr) \ *ptr; \ {ptr ## _DEREF_PTR \ attempt_to_dereference_pointer_ ## ptr;} int main(int argc, char *argv[]) { DEREF_PTR(int*, x) NO_DEREF_PTR(int*, y); DEREFERENCE(x); DEREFERENCE(y); // will throw an error } 

This gives you the ability to perform static error checking. Of course, using this method, you will have to go out and change all your macro pointers declarations, which is probably not very fun.

Edit: As described in the comments.

 #define READABLE_PTR(type, ptr) type ptr; \ typedef char ptr ## _READABLE_PTR; #define NON_READABLE_PTR(type, ptr) type ptr; \ #define GET(ptr) \ *ptr; \ {ptr ## _READABLE_PTR \ attempt_to_dereference_non_readable_pointer_ ## ptr;} #define SET(ptr, value) \ *ptr = value; int main(int argc, char *argv[]) { READABLE_PTR(int*, x) NON_READABLE_PTR(int*, y); SET(x, 1); SET(y, 1); int foo = GET(x); int bar = GET(y); // error } 
+6
Apr 17 '13 at 1:16
source share



All Articles