Search code examples
cstructarmbit-fieldsmmu

why is casting from an unsigned int to a struct that consists of bitfields making up an unsigned int not allowed?


I have looked at the recommendations for both my question and searched for my question's headline but did not find anything related. The line I am confused about is below the comments in mmu.c. I want to set up the MMU translation table on an ARM v7 32-bit processor. The function void map_virt_to_phys_adr(uint32_t virt_adr, uint32_t phys_adr, uint32_t AP, uint32_t PXN, uint32_t XN) takes both the virtual and physical address as parameters and permission flags. It simply creates an entry in the translation table that has the offset of the physical/virtual address (they need to be the same). I do not understand why the compiler gives the errors: a cast from the (L1_table_entry) (section_base_adr | (phys_adr & L1_table_index)) to L1_table_index is not allowed (there is no explanation). And why it says that a ')' is expected at L1_table_index in (phys_adr & L1_table_index)).

// mmu.h
#define L1_section_index ((uint32_t)1 << (21)) - 1;
#define L1_table_index ~L1_section_index;

typedef struct L1_table_entry{
    uint32_t PXN:1;
    uint32_t unused_1:3;
    uint32_t XN:1;
    uint32_t DOMAIN:4;
    uint32_t unused:1;
    uint32_t AP_0_1:2;
    uint32_t unused_2:3;
    uint32_t AP_2:1;
    uint32_t unused_3:4;
    uint32_t base_address:12;
}L1_table_entry;

typedef struct L1_fault_entry{
    uint32_t fault:2;
    uint32_t unused:30;
}L1_fault_entry;

enum MMU_entry_type {L1_ENTRY, L1_FAULT};

typedef struct MMU_entry{
    union {
        L1_table_entry L1_entry;
        L1_fault_entry L1_fault_entry;
    };
    enum MMU_entry_type type;
} MMU_entry;

// ...
//mmu.c

void map_virt_to_phys_adr(uint32_t virt_adr, uint32_t phys_adr, uint32_t AP, uint32_t PXN, uint32_t XN){
    uint32_t section_index = virt_adr & L1_section_index;
    uint32_t table_index = virt_adr & L1_table_index;
    uint32_t section_base_adr = phys_adr & L1_table_index;
    // cast to L1_table_entry is not allowed. Expected a ')' at L1_table_index.
    L1_table_entry =  (L1_table_entry) (section_base_adr | (phys_adr & L1_table_index));
    L1_table[table_index] = phys_adr & L1_table_index;
}

Edit: I am interested in the reasons for the errors and a solution :)


Solution

  • Casting to or from a struct is not allowed.

    Unless the type name specifies a void type, the type name shall specify qualified or unqualified scalar type and the operand shall have scalar type.

    Use a union for type punning.

    Or, if you are feeling adventurous, create an integer variable, take its address, cast it to a pointer to struct, and dereference the result. It is UB but will probably work.