On 02/22/18 12:52, Leif Lindholm wrote:
> On Thu, Feb 22, 2018 at 09:34:05AM +0100, Laszlo Ersek wrote:

>>> But that brings back the complication as to how we have a driver that
>>> needs an LE IO library to write output, and a BE IO library to
>>> manipulate the hardware.
>> Can you please explain the "write output" use case more precisely?
>> My thinking would be this:
>> - Use the IoLib class directly for "writing output" in little endian
>> byte order (which is still unclear to me sorry).
> If the IoLib class is mapped to a an instance that byte-swaps (hereto
> referred to as BeIoLib if IoLibSwap is unsuitable), would we not then
> end up mapping the non-swapping, currently implemented in
> BaseLibIoIntrinsic, variant as BeIoLib? Or if not, do we end up
> needing to duplicated all IoLib implementation .infs to provide an
> IoLib and a BeIoLib for each?
> It's at that point I burst an aneurysm.
> Am I overthinking/underthinking this?

We need two library classes, one for talking to LE devices and another
to BE devices. These should be usable in a given module at the same
time, as Ard says.

Both library classes need to work on both LE and BE CPUs (working from
your suggestion that UEFI might grow BE CPU support at some point).
Whether that is implemented by dumb, separate library instances
(yielding in total 2*2=4 library instances), or by smart,
CPU-endianness-agnostic library instances (in total, 2), is a different

Note that such "smarts" could be less than trivial to implement:
- check CPU endianness in each library API?
- or check in the lib constructor only, and flip some function pointers?
- use a dynamic PCD for caching CPU endianness?
- use a HOB for the same?
- use a lib global variable (for caching only on the module level)?

I think the solution that saves the most on the *source* code size is:
- introduce the BeIoLib class
- duplicate the MMIO functions from BaseIoLibIntrinsic to the one
  BeIoLib instance that we introduce
- modify the MMIO functions in *both* lib instances (original LE, and
  new BE), like this:

  - If the CPU architecture is known to be bound to a single endianness,
    then hardcode the appropriate operation. This can be done with
    preprocessor macros, or with the architecture support of INF files /
    separate source files. For example, on IA32 and X64, the IoLib
    instance should work transparently, unconditionally, and the BeIoLib
    instance should byte-swap, unconditionally.

  - On other CPU arches, all the wider-than-byte MMIO functions, in
    *both* lib instances should do something like this:

    // at file scope
    STATIC CONST UINT16 mOne = 1;

    // at function scope
    if (*(CONST UINT8 *)&mOne == 1) {
      // CPU in LE mode:
      // - work transparently in the IoLib instance
      // - byte-swap in the BeIoLib instance
    } else {
      // CPU in BE mode:
      // - byte-swap in the IoLib instance
      // - work transparently in the BeIoLib instance

edk2-devel mailing list

Reply via email to