Jason’s remarks prompted me to dig into this a bit more even though that was a rabbit hole I didn’t care to venture into.
Using setkeycodes is about as low level as it can get, I’m afraid. Everything I’ve seen seems to say that yes indeed, the scan codes are a function of the keyboard hardware and associated controller. Back in the bad old days there was actually an intel 8042 microcontroller at ports x60 and x64. Even today, the Linux AT keyboard driver talks through those ports to something that behaves like the legacy controller.
Effectively, the controller scans the key switch matrix and provides a stream of scan codes as key presses occur, and those scan codes are more or less standardized based on the keyboard vendor’s model and layout. That being given, it is up to either the BIOS or the OS keyboard driver to map the scan codes to the key numbers which are used by all the higher layers of abstraction from the Linux virtual console up through X apps.
When you’re using a GRUB2 console, the scan code to key number translation must be done by SeaBIOS which Purism has bundled with Coreboot. They will have taken care to make sure the resulting key number matches the legend on the key. This explains why it works “right” for GRUB2.
John Savard has an excellent exposition of how keyboard evolution brought us to the layouts and scan code sets we have today at http://www.quadibloc.com/comp/scan.htm. Based on his description of the standard 101-key keyboard and the international variants, it seems like the '13v2 keyboard is a compact version of the international UK keyboard. Search on his page for “International Keys” and pay particular attention to his description of the Int 1 and Int 2 keys. See also the layout diagrams below that comparing the US and UK variants and the scan codes assigned to each key location.
So we have an International UK keyboard here, but space constraints don’t allow for the Int 1 key near the left shift, which would be the \| key in the UK market. The Int 2 key is normally located where we expect to find \| and does produce scan code x2B, but in the UK market is normally <>. So it seems the vendor took the Int 1 key (UK \|) from its location near left shift and put is where we (US) expect to see that key. The Int 1 key they dispensed with altogether. So there is no physical key present on this keyboard which produces scan code x2B, and consequently no standard keyboard layout file is going to give us \|.
That’s why we have to accept the one and only scan code the key does produce (x56) and use setkeycode to assign it the desired key number. This tells the kernel to modify its scan-to-keyboard-number translation table. But Jason is right, this doesn’t help in early boot where only the initramfs is mounted. If you have \ or | in your LUKS disk passphrase you’re out of luck. If you boot into single user mode, rc.local is not run and you’re out of luck–at least you can run setkeycodes manually at that point.
It would be possible to modify the keyboard layout files used in the initramfs and later so that key number 86 yields \|. But if you have a use case where you sometimes need to switch layouts you’ll have mutliple files to maintain, and you might be doing manual edits every time they’re updated.
Better would be to somehow work the setkeycodes run into the initramfs somehow. I’m looking into the Fedora case–there it’s probably just a matter of adding a new dracut module. But I’m not familiar enough with how Debian derived distros build their initramfs to comment on that case.
More later when/if I get something working.