0

I am trying to write a test app that unmarshals a binary blob. The blob is generated by a low-level h/w interface. Based on the c header I found that the binary blob is defined in this way,

typedef struct Report
{
    uint32_t SnpVersion;
    uint32_t SnpGuestSvn;
     union {
            uint64_t ModelVer_t;
            struct {
                uint64_t AbiMinor:8;
                uint64_t AbiMajor:8;
                uint64_t TTAllowed:1;
                uint64_t RrvdTrue:1;
                uint64_t MigrateTTAllowed:1;
                uint64_t DebugAllowed:1;
                uint64_t RrvdFalse:44;
            } TuneConfig;
          } TunePolicy;
    union {
        uint64_t Asuint64_t;
        struct {
            uint64_t TTEnabled:1;
            uint64_t RRvd:63;
        } ChannelConfig;
    } ChannelPolicy;
   uint8_t  PolicyDigest[48];
 }

I need to do unmarshalling of the binary blob in c#. Based on the ref at this link, https://learn.microsoft.com/en-us/dotnet/framework/interop/marshalling-classes-structures-and-unions, I should be able to generate it. However, I am still not sure about the syntax of representing the AbiMinor:8; The reserved bits in managed code.

Bhupesh Pant
  • 4,053
  • 5
  • 45
  • 70

1 Answers1

2

The way to represent the reserved bits is through the [StructLayout] and [FieldOffset] attributes applied to to the fields of a struct. However ... this union has several fields that are less than a byte, so I'd recommend not doing that at all because [FieldOffset] is in units of whole bytes, so you're not buying yourself anything by trying to replicate this union.

If you're going to marshal a struct, then just use primitives of the appropriate size and write code to get at the underlying values.

[StructLayout(LayoutKind.Sequential)]
public struct ReportManaged
{
    // the actual fields of the struct
    private readonly uint SnpVersion;
    private readonly uint SnpGuestSvn;
    private readonly ulong TunePolicyUnion;
    private readonly ulong ChannelPolicyUnion;
    private fixed byte[48] PolicyDigest;

    // properties to access the struct's data
    public ulong ModelVer => TunePolicy;
    
    public byte TuneConfig_AbiMinor => //most significant byte of TunePolicyUnion
    public byte TuneConfig_AbiMajor => //2nd most significant byte of TunePolicyUnion
    public bool TuneConfig_TTAllowed => //17th bit of TunePolicyUnion
    public bool TuneConfig_RvrdTrue => //18th bit of TunePolicyUnion
    public bool TuneConfig_MigrateTTAllowed => //19th bit of TunePolicyUnion
    public bool TuneConfig_DebugAllowed => //20th bit of TunePolicyUnion
    public bool TuneConfig_RrvdFalse => //rest of TunePolicyUnion

    public ulong ChannelPolicy_AsUInt64 => ChannelPolicyUnion;
    
    public bool ChannelConfig_TTEnabled => //most significant byte of ChannelPolicyUnion
    public ulong ChannelConfig_RRvd => //rest of ChannelPolicyUnion
}

Another option, and the one I prefer to use myself for this sort of thing, is to marshal the Report over simply as a byte array then read it like it's a stream, building a C# object as you go along. The union defines a message format. Each message starts with a 32-bit SnpVersion and a 32-bit SnpGuestSvn, and then it has either a 64-bit Model Version or a Tune Config, and then a 64-bit field or a ChannelConfig, and it always ends with 48 bytes of data.

John V
  • 1,286
  • 9
  • 15