heads icon indicating copy to clipboard operation
heads copied to clipboard

WiP change Intel PHYsical mac (gbe) from Heads

Open tlaurion opened this issue 1 year ago • 83 comments

- Addition of nvmutil (nvm) from osboot project to play with gbe (Thanks @githubisnonfree!)
- Addition of ifdtool from coreboot project to extract gbe
  - As of now, its implemented in a hacky way:
    - ifdtool dir is copied over ifdtool_cross at coreboot's module configure step
    - then initrd packing step checks for CONFIG_NVMUTIL and builds and pack ifdtool_cross/ifdtool
    - As a result, what is build under build/coreboot/$BOARD is coreboot's real, where build/coreboot/ content follows Makefile rules
- CONFIG_NVMUTIL in board config adds both ifdtool_cross/ifdtool and nvmutil into initrd
- Added CONFIG_NVMUTIL to all hotp-maximized boards (to test for
 size changes)

Manually tested (working!):
- backup rom from: `flashrom -p internal -r /tmp/backup.rom`
- go to that dir: `cd /tmp`
- extract gbe from ifdtool on backup.rom: `ifdtool -x backup.rom`
- show current PHY mac address: `nvm flashregion_3_gbe.bin dump`
- change mac from nvm on extracted gbe (random): `nvm flashregion_3_gbe.bin setmac`
- show mac to be changed: `nvm flashregion_3_gbe.bin dump`
- insert modified gbe into backup.rom.new with ifdtool: `ifdtool -i gbe:flashregion_3_gbe.bin backup.rom`
- flash back modified gbe only through flashrom: `flashrom -p internal --ifd -i gbe -w backup.rom.new`

Question now is what is the proper workflow from here. New menu options from configuration menu?

  • Generate new MAC address -> show mac address : flash ROM with new mac address (Yes: flash, No: Generate new MAC)
  • Specify new MAC Addresss -> go to console prompt for user to set new mac, validate with nvm output on extracted gbe.bin insertion -> flash if valid?

Fixes #916

tlaurion avatar Aug 01 '22 21:08 tlaurion

the flash rom gui menu could use a tweak already IMO. i would like to see all options merged into submenus. It would be nice have it go like this:

Flash New Rom > GPG Settings > MAC Settings > Mount USB and Select rom/GPG > Confirmation

GPG Settings:
- Keep Current GPG
- Add New GPG from USB
- Clean Flash (generate GPG later)

MAC Settings:
- Keep Current MAC
- Generate New MAC
- Specify New MAC

**Mount USB and Select rom/GPG**

Confirmation Dialog: 
- Old Heads vs New Heads version
- Old GPG vs New GPG
- Old MAC vs New MAC

**Flash/Cancel**

first option on both prompts retains settings, making routine updates as simple as possible.

maybe worth noting that the "add to standalone bios and re-flash" options in the existent gpg options menu could be removed as redundant if the Flash menu gets retooled this way.

weyounsix avatar Aug 02 '22 09:08 weyounsix

@tlaurion I like this approach. I would be concerns that if a user flashed a maximized version over the standard HEADS rom then modified the MAC address before rebooting then the depending on if flashrom read the IFD from the buffer or from the chip may write to the wrong location. This would prevent that.

An input mask allowing only hex-ish characters (0-9A-F and a-f, :, ) where any missing characters are padded with zeros then sent through sed/tr to be only `0-9A-F``, would prevent the need for a complicated MAC check.

Thrilleratplay avatar Aug 02 '22 11:08 Thrilleratplay

i try to be as correct as possible with what status is returned on exit from nvmutil. you can pretty much just run it and do || in your shell script to make it crap out if there's a problem (e.g. invalid checksum, invalid mac address) etc

so like, your build system would let the user specify a mac address and your build system could just...

./nvm gbe.bin setmac macaddresshere || exit 1

or whatever you want after that ||

that's the way i do it in osboot, the simple way. fail early, fail hard is the way i generally do things

githubisnonfree avatar Aug 02 '22 16:08 githubisnonfree

by the way, this should work on basically all intel ifd based machines, all those gbe regions have the same layout in them, and nvmutil intentionally dosen't set anything unless the checksum is correct, to guard against a future possibility that intel changes it; presumably they'd use a different checksum validation then, or maybe put it in a different place

but it should work just fine on all the thinkpads you support. works on x200/t400 etc aswell. i also use it on t440p machines

githubisnonfree avatar Aug 02 '22 16:08 githubisnonfree

i actually checked linux src and pretty much all these nics use that same algorithm for checksum: the words must add up to 0xBABA, and mac address is first 3 words

(linux verifies the checksum before doing anything with the nic, and will actually refuse to set up your nic if both checksums are bad)

githubisnonfree avatar Aug 02 '22 16:08 githubisnonfree

btw small nitpick it's call nvmutil, not nvmtool

githubisnonfree avatar Aug 02 '22 18:08 githubisnonfree

@tlaurion I like this approach. I would be concerns that if a user flashed a maximized version over the standard HEADS rom then modified the MAC address before rebooting then the depending on if flashrom read the IFD from the buffer or from the chip may write to the wrong location. This would prevent that.

@Thrilleratplay : Yeah thought about that. We could wrap around checking the output of flashrom -p internal output to see if no warning are given there.

An input mask allowing only hex-ish characters (0-9A-F and a-f, :, ) where any missing characters are padded with zeros then sent through sed/tr to be only `0-9A-F``, would prevent the need for a complicated MAC check.

@Thrilleratplay : We could, or we could base ourselves on the validation made by nvmutil (nv). I prefer the latest.

Long term (PR welcome) we could also borrow other MAC randomization tweaks. Some router will not permit some non existing OUI. So it would be logical to randomize only in the "Intel Corp" OUI space but there I do not want to go beyond user's needs?

i try to be as correct as possible with what status is returned on exit from nvmutil. you can pretty much just run it and do || in your shell script to make it crap out if there's a problem (e.g. invalid checksum, invalid mac address) etc

so like, your build system would let the user specify a mac address and your build system could just...

./nvm gbe.bin setmac macaddresshere || exit 1

or whatever you want after that ||

that's the way i do it in osboot, the simple way. fail early, fail hard is the way i generally do things

@githubisnonfree : The goal here is to build against musl-cross-make in the firmware and provide tools for users to change PHY whenever they want and flash back internally from flashrom. We could permit the user to build a ROM with a custom PHY, but that would go against reproducible goal and mode support. Unfortunately in Heads current use case, since we build ifdtool and nvutil from musl-cross-make to be packed in rom (against musl libc) we could document the process of building from the builder machine, but I kinda not see the point if the firmware is doing the proper thing, that being to pack a gbe.bin that is setuped as "DE:AD:C0:FF:EE" in the goal of having everyone having maximized builds to have reproducible roms in the long term, even if those ROMs contain a GBE, ME, IFD and BIOS regions. And have the user decide if he wants to change it. Let's not forget that user's are better having a randomized MAC setuped through NetworkManager nowadays, this feature is mainly aimed as a safeguard, and for Heads users having multiple machines to be able to connect multiple machines on LAN if they do not apply in software mac randomization.

The problem I foresee in the future is that Coreboot is starting to measure other components. 4.17 is now measuring CBFS content, and ME region is desired to be measured since forever. I do not see why GBE (and everything Intel IFD) not being measured at some point. That will modify PCR2 as of now, and in the future we will need to do some tpmfuturecalc to take that into consideration for future upgrades.... Should probably be considered in other future issues, just a note.

@weyounsix

the flash rom gui menu could use a tweak already IMO. i would like to see all options merged into submenus. It would be nice have it go like this:

Flash New Rom > GPG Settings > MAC Settings > Mount USB and Select rom/GPG > Confirmation

GPG Settings:
- Keep Current GPG
- Add New GPG from USB
- Clean Flash (generate GPG later)

MAC Settings:
- Keep Current MAC
- Generate New MAC
- Specify New MAC

**Mount USB and Select rom/GPG**

Confirmation Dialog: 
- Old Heads vs New Heads version
- Old GPG vs New GPG
- Old MAC vs New MAC

**Flash/Cancel**

first option on both prompts retains settings, making routine updates as simple as possible.

maybe worth noting that the "add to standalone bios and re-flash" options in the existent gpg options menu could be removed as redundant if the Flash menu gets retooled this way.

Unfortunately, I am not sure this would please all stake holders here, me not being convinced with the approach either, since those actions are normally desired separately, and one rarely needs to change public key (and if he desires so, can do it under GPG options).

One that wants to change all of this would most definitely simply flash a clean rom without keeping settings and hit the OEM Factory reset/Re-Ownership wizard to accomplish all of this, minus the PHY MAC changing that is subjected here.

@Thrilleratplay @githubisnonfree @weyounsix What about simply

  • adding a new submenu only if CONFIG_NVMUTIL exists? (BOARDS should not add NVMUTIL if not supported to Change PHY MAC Address (CONFIG_NVMUTIL would need to be exported then to be under /etc/config)
  • Add logic under flash.sh to also take into account CONFIG_NVMUTIL presence, check if mac is different then DE:AD:C0:FF:EE through ifdtool -x backup.com, nvm showmac flashregion_3_gbe.bin and if different then rom, reinject user defined PHY MAC from actual rom? (Backup?)

That would not require any other logic to be modified, would have the MAC persist between firmware upgrades and let users decide if they want to change their mac address? Changing menu and orders of menu options is a complicated matter (check past PRs), where Purism and other boards constructors might not even have a Compatible Intel Ethernet card (those connectors tend to disappear, and if Ethernet port is there, they are not necessarily Intel made).

I prefer small approach and move from there.

As far as this PR goes, it is actually feature complete (but xx20 and xx30 board configurations should add the feature disabled). A problem here is that I am pretty sure xx20 board owners will as of today not be able to even add a public key (with a picture, or circle of trust signatures included in their public key) might not even fit since this PR is including ifdtool, as all other boards (since ifdtool addition is added for ALL, including platforms that do not have Intel Firmware Descriptor (kgpe-d16 boards, Talos II etc are now including a tool that they will never use.

So basically, next steps prior of being even able to merge this is:

  • Makefile find a way to externalize ifdtool inclusion under nvmtool module? Some ideas based on the current hack on global Makefile, anyone?

tlaurion avatar Aug 02 '22 19:08 tlaurion

Some notes on actual problems that won't permit t420/x220/w520 to be usable.

Master build log on x220-maximized:

Name                           Offset     Type           Size   Comp
cbfs master header             0x0        cbfs header        32 none
fallback/romstage              0x80       stage           84972 none
cpu_microcode_blob.bin         0x14d00    microcode       26624 none
fallback/ramstage              0x1b580    stage           97628 none
config                         0x33340    raw               776 none
revision                       0x33680    raw               691 none
fallback/dsdt.aml              0x33980    raw             14615 none
vbt.bin                        0x37300    raw              1400 LZMA (3985 decompressed)
cmos_layout.bin                0x378c0    cmos_layout      1848 none
fallback/postcar               0x38040    stage           25816 none
fallback/payload               0x3e580    simple elf    7209415 none
(empty)                        0x71e780   null            71192 none
bootblock                      0x72fdc0   bootblock       65536 none

This PR x220-maximized build log:

FMAP REGION: COREBOOT
Name                           Offset     Type           Size   Comp
cbfs master header             0x0        cbfs header        32 none
fallback/romstage              0x80       stage           84972 none
cpu_microcode_blob.bin         0x14d00    microcode       26624 none
fallback/ramstage              0x1b580    stage           97639 none
config                         0x33340    raw               776 none
revision                       0x33680    raw               691 none
fallback/dsdt.aml              0x33980    raw             14615 none
vbt.bin                        0x37300    raw              1400 LZMA (3985 decompressed)
cmos_layout.bin                0x378c0    cmos_layout      1848 none
fallback/postcar               0x38040    stage           25816 none
fallback/payload               0x3e580    simple elf    7225799 none
(empty)                        0x722780   null            54808 none
bootblock                      0x72fdc0   bootblock       65536 none

So the addition of ifdtool alone consumes from the free space available: 71192 - 54808 = 16384 bytes 54808 bytes is still ok when we add stuff into kernel or initrd (xz compressed) but never forget that when we add stuff with cbfs, those are uncompressed. Let's also remember this is not x220-hotp-maximized which requires additional hotp binaries and libs:

x220-hotp-maximized build log from this PR

Name                           Offset     Type           Size   Comp
cbfs master header             0x0        cbfs header        32 none
fallback/romstage              0x80       stage           84972 none
cpu_microcode_blob.bin         0x14d00    microcode       26624 none
fallback/ramstage              0x1b580    stage           97639 none
config                         0x33340    raw               786 none
revision                       0x336c0    raw               691 none
fallback/dsdt.aml              0x339c0    raw             14615 none
vbt.bin                        0x37340    raw              1400 LZMA (3985 decompressed)
cmos_layout.bin                0x37900    cmos_layout      1848 none
fallback/postcar               0x38080    stage           25816 none
fallback/payload               0x3e5c0    simple elf    7233479 none
(empty)                        0x7245c0   null            47064 none
bootblock                      0x72fdc0   bootblock       65536 none

Only has 47064 bytes free after ifdtool inclusion from global Makefile change.


So merging this PR would be the first case where xx30 and xx20 would differ in features that can be offered. #590 and some of its subtasks will probably need to be fixed prior of a merge of the current PR.

More direct todos:

  • [x] Seperate ifdtool building under Makefile or make it conditional on CONFIG_NVMUTIL

  • [ ] Work, test https://github.com/osresearch/heads/pull/1121 and merge

  • [ ] Work on https://github.com/osresearch/heads/pull/1184, test and merge

tlaurion avatar Aug 02 '22 19:08 tlaurion

why not just have a script

download it into tmpfs and run it

(after checking signatures)

also, i've recently started porting openbsd userland to linux+musl, i'm months away from completing it probably but i want to replace busybox. when you take all the extra non-posix stuff away until you're left with only the same features as busybox, openbsd code is actually smaller in almost every case i've checked - even ksh is the same size as busybox sh, and that's with all the bells and whistles

i hope to have something to show later this year, that people can really use on their machines. i'm doing it for my own linux i'm working on, but you can be sure i'll be showing it to heads first: i'm doing it because i have the same problem as you: limited flash space. i want a flash distro for osboot, like heads though i'm basing my work off of alpine locally. i'll release later in the year when i have something complete

githubisnonfree avatar Aug 02 '22 21:08 githubisnonfree

have a look at chimera linux, they have a few nice things in it

in particular, they have almost (if not entirely, haven't looked in great detail yet) a complete freebsd-based userland on top of linux and musl

they recently ported freebsd sh to linux

freebsd sh is about 12k source lines of code, busybox sh is like 19k last time i checked. you might find more gold like that in chimera linux to try out in your project

here's posix cat for free:

https://vimuser.org/cat.c.txt

(this is my one, after stripping down the openbsd one)

edit: and to be clear i'm recommending this because i'm aware your distro has the problem: 8MB flash setups have limited feature sets due to low flash space. i'm told alpine linux is also working on their own userland but i have no idea about the status of that

githubisnonfree avatar Aug 02 '22 21:08 githubisnonfree

also, i've been experimenting with various compilers and i've found that in a lot of cases, tcc produces smaller binaries than gcc or clang

in cases where gcc wins (if one of them beats tcc, it's always gcc) like with -Os and LTO, that's uncompressed binaries, those tcc-built binaries when compressed (e.g. in CBFS with LZMA!) really get quite small indeed

freebsd sh (alone) can be made to go down to about 150KB potentially and that's when i built for x86_64, for i686 you can probably get much smaller, whereas busybox ash probably can't get much smaller than say 230KB. these are uncompressed sizes btw

githubisnonfree avatar Aug 02 '22 21:08 githubisnonfree

how small? well for a program that is about 300KB compiled with GCC or clang, it's not uncommon for me to see that tcc is about 150KB, half the size, and that's with stuff like -Os and -flto used in GCC

you should look into using tcc if you haven't already. it could really save a lot of space in rom when putting all these utils together. tcc is great (also called tinycc)

githubisnonfree avatar Aug 02 '22 21:08 githubisnonfree

@githubisnonfree all those comments unfortunately will be lost under a PR once merged. I think you of all other users (libreboot and osboot main collaborator) understand this.

All those comments are related to #590 and should be added there.

Thanks for your understanding and motivation. Getting away of dropbear would be interesting, while we accomplished a state where dropbear being stripped, as all other binaries and libraries also compressed with xz -9 and -Os missing (700kb freed) I'm not sure moving from musl to tcc is recommended here, where musl have a really good reputation on both security stability and size.

Yet again, #599 would be the place to debate those ideas not here, which will be lost in space and time soon enough tracking those ideas.

tlaurion avatar Aug 02 '22 21:08 tlaurion

oh, well musl is a libc and tcc is a c compiler

i don't use github so i don't understand its nuances. i'll post on the issues instead

githubisnonfree avatar Aug 02 '22 21:08 githubisnonfree

@githubisnonfree sorry for the confusion, heads uses local GCC to build musl-cross-make and then builds everything with it, including coreboot's buildstack which then builds itself with it.

Not against those ideas at all, just telling you a PR (merge request equivalent) is not the place to track sidequest ideas.

Issues are. As on notabug ;)

tlaurion avatar Aug 02 '22 22:08 tlaurion

i mean notabug sucks. normally i just work with people directly on irc or they email me

githubisnonfree avatar Aug 02 '22 22:08 githubisnonfree

Unfortunately, I am not sure this would please all stake holders here, me not being convinced with the approach either, since those actions are normally desired separately, and one rarely needs to change public key (and if he desires so, can do it under GPG options).

for context: my thinking here was merely that it would be nice to only have to flash the bios once for a totally new configuration. i find it strange that gpg operations are separate, considering any such operation involves reflashing. i wouldn't advocate removing the gpg operations menu, but just to merge the relevant options for "standalone bios images" into the flash menu, so that if a new rom is being flashed (not the "running bios" gpg operations) then you are able to fully configure it in one go.

understood and no sweat if this is not desirable. just a thought.

weyounsix avatar Aug 03 '22 07:08 weyounsix

@githubisnonfree sorry for the confusion, heads uses local GCC to build musl-cross-make and then builds everything with it, including coreboot's buildstack which then builds itself with it.

Not against those ideas at all, just telling you a PR (merge request equivalent) is not the place to track sidequest ideas.

Issues are. As on notabug ;)

by the way, i've made a note about musl-cross-make. it sounds like exactly the sort of thing i need in my project

githubisnonfree avatar Aug 03 '22 09:08 githubisnonfree

@tlaurion While having a difference between the xx30 and xx20 isn't ideal, I think using nvmtool is the best approach given the restrains of size. Also keep in mind that is it isn't a feature that is completely denied to the xx20 users as they could still use do this using a prebuilt version in external storage or through an OS. They are only losing the convenience of it being built in. Hopefully, this will be short term.

Thrilleratplay avatar Aug 03 '22 11:08 Thrilleratplay

regarding randomization

i think the best way to have random mac addresses would be to implement it in nvmutil. command syntax example:

./nvm gbe.bin setmac 00:11:22:33:44:55

This example above is setting a static address. What if I made it possible to do:

./nvm gbe.bin setmac ??:??:??:??:??:??

or maybe

./nvm gbe.bin setmac 00:?1:??:2?:33:4?:5?

See that?

Basically, any character that is ? would have nvmutil to randomly generate a number.

This is the best way, because you might want a certain pattern. For example, some Intel NICs (on x200/t400 thinkpads) start with 00:1f:16:xx:xx:xx where x characters can be anything.

githubisnonfree avatar Aug 04 '22 22:08 githubisnonfree

Some router will not permit some non existing OUI. So it would be logical to randomize only in the "Intel Corp" OUI space but there I do not want to go beyond user's needs?

The list of Intel Corp possible OUI is large.

This is the best way, because you might want a certain pattern. For example, some Intel NICs (on x200/t400 thinkpads) start with 00:1f:16:xx:xx:xx where x characters can be anything.

@githubisnonfree absolutely, this would fit the need, where collisions on a shared prefix should not happen on LAN.

Under Heads, we could lspci and apply Intel OUI accordingly, and/or define randomization patterns under Heads board config if users want to limit range as well.

From my limited tests, my own locally used switches never stopped me from using totally randomized Mac addresses in the past, as opposed to WiFi controllers. From my limited experience, and outside of NAC enforced network limitations (which reuses wireshark OUI list as pointed in previous post) I do not think wired MAC are filtered, but I might be wrong for certain corner cases (my Cisco switch for example permitted total local randomization as used under /etc/functions as of now under network-recovery-init under Heads, so took it as a positive PoC and good enough)

Would be a nice to have, definitely, though!

tlaurion avatar Aug 04 '22 22:08 tlaurion

well C has rand() which is pretty decent. the only question is, what to use as a seed value.

a most conservative choice would be to simply fetch the current time (unix timestamp).

of course, another way is to directly read from /dev/urandom

i will think about this

githubisnonfree avatar Aug 04 '22 22:08 githubisnonfree

there are libs that do randomization but for our purposes, what i want is something simple and reasonably random. this isn't AES

githubisnonfree avatar Aug 04 '22 22:08 githubisnonfree

i think a good sane approach is:

read from /dev/urandom, get numbers from that. urandom is sufficiently random for our purposes

or just return -2 if can't read from urandom

currently, mac address validation returns -1 if invalid mac passed. i'm designing my patch to set -2 if randomization is impossible for whatever reason, then if -2, validation says: randomization failed. then it exists with non-zero status

basically an FU to non-unix systems

githubisnonfree avatar Aug 04 '22 23:08 githubisnonfree

@tlaurion i did it

patch: https://notabug.org/osboot/nvmutil/commit/40f25c70d5fb7aeb6e56abf91e562a901c743e42

This adds randomization support for setmac, with the scheme previously described.

githubisnonfree avatar Aug 05 '22 01:08 githubisnonfree

@tlaurion check again nvmutil git repo. i optimized it a ton. 11224kb compiled with tcc on my x86_64 machine

i'm planning to revive nvmutils too, with these optimized techniques from nvmutil. the benefit there is i could make for you much smaller binaries that only do single tasks

anyway, compressed with lzma, nvmutil when compiled with gcc will currently get you about 5kb, or with tcc, about 3kb. tested: with xz -9e

githubisnonfree avatar Aug 07 '22 05:08 githubisnonfree

i'm going to experiment further, later; i will see if using mmap for files (instead of read/write) might yield a smaller binary. that's about all i can do before i start breaking features. this is the most riced gbe mac address changer ever

githubisnonfree avatar Aug 07 '22 05:08 githubisnonfree

@githubisnonfree as stated under https://github.com/osresearch/heads/issues/590#issuecomment-1047293706, heads challenge is to optimize for space bigger tools and libraries from their modules definition. That means removing unused features of such tools, optimizing compilation for space, removing unused functions post linking and/or compressing more or other tricks.

I see your attempt to reduce from 5k to 3k as input to be able to reduce the biggest tools and libraries we currently need, eg lvm cryptsetup and other gpg toolstack required tools.

But the interest here is not to pass from 5k to 3k. As issues/PR pointing to/from https://github.com/osresearch/heads/issues/590, by reducing kernel size (compressed by xz) and removing in kernel compiled stuff, big gains can be accomplished there with a bigger ratio gain in total space wins.

Moving to tcc is interesting, but would require migrating all modules where libc is one of the biggest thing we compile for other modules to be linked against it.

tlaurion avatar Aug 07 '22 16:08 tlaurion

ok, but nvmutil has been optimized for size now, and i just woke up as i write this. gonna finish nvmutil using mmap to see if that reduces size further

nvmutil could be modified to only have the ability to set mac addresses, like nvmmac from nvmutils. to reduce size of the binary further (even when using gcc)

githubisnonfree avatar Aug 07 '22 19:08 githubisnonfree

@tlaurion i made a new release of nvmutil. version 20220808

https://notabug.org/osboot/nvmutil/src/20220808

Changes:

https://notabug.org/osboot/nvmutil/src/20220808/ChangeLog.md

Summary:

  • Vastly reduced code/binary size
  • OpenBSD pledge used (ifdef rule used, so it still compiles on linux/freebsd)
  • Better/safer file handling
  • Makefile: -Os flag used instead of -O2
  • Stricter exit status, upon program termination
  • EDIT: and of course, random MAC addresses are now possible

I highly recommend upgrading to this version. Also, I'm contemplating making a standalone binary that just changes the mac address, for even smaller binaries.

Regarding ifdtool: want me to fork it and make it really small? Tell me whether you have any IFDv2 systems in heads. If it's just v1, that simplifies things a lot.

githubisnonfree avatar Aug 08 '22 06:08 githubisnonfree