Info about endianness safe coding?

Hello Dominic

Can you give a short description of the correct way to handle hosts with different endianness ?

I see you are making changes like:

target->type->read_memory(target, MC_FSR, 4, 1, (u8 *)&fsr);

to:

fsr = target_buffer_get_u32(target, (u8 *)&fsr);

Greetings

Magnus

Hello Magnus,

I’ve been writing (as on paper) about what I’d like the different API’s to use, and what not. Sorry I didn’t publish this earlier.

The jtag_XXX functions take u8* (pointers to unsigned 8-bit datatype) arguments to little-endian data, as that’s the “natural” endianness for JTAG data (LSB first).

Memory read/write functions (target->type->read_memory/write_memory) take an u8* argument pointing to a buffer in target endianness. This is to support GDB, which uses plain byte streams to transfer data to/from the target’s memory (among other things. in general, we can’t know what data is inside the buffer).

All functions that specify u32- or u16- take data in host-endianness. The arm[79]tdmi_clock_<out|in> functions uses handlers to translate the data captured from the JTAG interface (little-endian) to the hosts endianness.

The arm[79]tdmi_clock_data_in_endianness are used to read data in 8, 16 or 32 bits quantities in target endianness, to faciliate loading of target-endian buffers directly.

If flash code for example wants to read a 32-bit word from the target’s memory space, it can either use target->type->read_memory, reading the data in the target’s endianness, and convert it using target_buffer_get_u32, or target_read_u[32|16|8] to read the data directly into a variable in host endianness.

Regards,

Dominic


edit:

This isn’t complete yet. I’ll add to this list while I’m going through my notes, possibly cleaning up the current code.

Here is an attempt to describe the idea a bit more structured:

  • JTAG

All JTAG operations take pointers to little-endian buffers (u8*). The in_handler member of the scan_field_t structure can be used to put captured data directly in a host-endian variable, or a target-endian buffer.

  • TARGET

The target interface (target_type_t, target_t, registers, algorithms) use little-endian buffers for values, to potentially allow the OpenOCD to be extended beyond 32 (64, 128) bit targets, for example in case of multi-media extensions. buf_get/set_u32 should be used to read/write these buffers.

Those interfaces currently using u32 for addresses would still have to be changed.

Target-internal functions can use whatever datatype fits the architecture, for example u32 in case of ARMv4/5. These values are stored in normal u32 variables, therefor in host-endianness. As target->type->read/write_memory operates on buffers in target-endianness, the convenience functions target_read/write_u8/6/32 have been added to directly read/write u8, u16 and u32 values in host endianness.

  • FLASH

Flash functions only make use of the external target interface, and therefor operate on buffers in target endianness. If flash code has to read/write memory mapped registers, it should use the target_read/write_u8/16/32 functions. Alternatively, it can call target->type->read/write memory directly, and use target_buffer_get/set_u8/16/32 on the memory buffer (that’s what I did when I started working on the at91sam7.c code. I completely forgot about these changes, and committed them together with the other changes. This is incomplete, and target_read_u32 would be more appropriate).

If code is to be executed on the target (flash write algorithms, for example), the opcodes are stored in u32 arrays (therefor in host endianness). Before writing these opcodes to the target, target_buffer_set_u32 has to be used to convert the opcodes to target endianness. See arm7_9_bulk_write_memory for an example.

Regards,

Dominic