[arch-projects] [mkinitcpio] [PATCH] Add 'microcode' hooks
Add early microcode support with early cpio. All files added under /early_root will be created as a separate cpio prepend to original one. Add a early_cpio file to early cpio to detect whether cpio contains early cpio, thus we can have lsinitcpio work normally like old one. --- .gitignore | 1 + Makefile | 9 +++- install/microcode | 24 +++++++++++ lsinitcpio | 35 +++++++++++++--- mkinitcpio | 13 +++++- skipcpio.c | 123 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 198 insertions(+), 7 deletions(-) create mode 100644 install/microcode create mode 100644 skipcpio.c diff --git a/.gitignore b/.gitignore index a814a0f..1e501f0 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ lsinitcpio.1 *~ *.bak mkinitcpio-*.tar.gz +skipcpio diff --git a/Makefile b/Makefile index 5af0eb2..a3a3591 100644 --- a/Makefile +++ b/Makefile @@ -20,7 +20,10 @@ DIRS = \ /usr/lib/systemd/system/shutdown.target.wants \ /usr/lib/tmpfiles.d -all: doc +all: doc skipcpio + +skipcpio: skipcpio.c + $(CC) skipcpio.c -o skipcpio MANPAGES = \ man/mkinitcpio.8 \ @@ -40,6 +43,7 @@ install: all < mkinitcpio > $(DESTDIR)/usr/bin/mkinitcpio sed -e 's|\(^_f_functions\)=.*|\1=/usr/lib/initcpio/functions|' \ + -e 's|\(^_f_skipcpio\)=.*|\1=/usr/lib/initcpio/skipcpio|' \ -e 's|%VERSION%|$(VERSION)|g' \ < lsinitcpio > $(DESTDIR)/usr/bin/lsinitcpio @@ -67,6 +71,8 @@ install: all ln -s mkinitcpio $(DESTDIR)/usr/share/bash-completion/completions/lsinitcpio install -m644 shell/zsh-completion $(DESTDIR)/usr/share/zsh/site-functions/_mkinitcpio + install -m755 skipcpio $(DESTDIR)/usr/lib/initcpio/skipcpio + doc: $(MANPAGES) man/%: man/%.txt Makefile a2x -d manpage \ @@ -76,6 +82,7 @@ man/%: man/%.txt Makefile clean: $(RM) mkinitcpio-${VERSION}.tar.gz $(MANPAGES) + $(RM) skipcpio dist: doc echo $(VERSION) > VERSION diff --git a/install/microcode b/install/microcode new file mode 100644 index 0000000..4dfd69e --- /dev/null +++ b/install/microcode @@ -0,0 +1,24 @@ +#!/bin/bash + +build() { + ucode_dir=(amd-ucode intel-ucode) + ucode_dest=(AuthenticAMD.bin GenuineIntel.bin) + + for idx in 0 1; do + fw=${ucode_dir[$idx]} + for fwpath in "${_d_firmware[@]}"; do + if [[ -d $fwpath/$fw ]]; then + add_dir '/early_root/kernel/x86/microcode' + cat $fwpath/$fw/* > $BUILDROOT/early_root/kernel/x86/microcode/${ucode_dest[$idx]} + fi + done + done +} + +help() { + cat <<HELPEOF +This hook generate early ucode update +HELPEOF +} + +# vim: set ft=sh ts=4 sw=4 et: diff --git a/lsinitcpio b/lsinitcpio index 9567b79..902bfe2 100755 --- a/lsinitcpio +++ b/lsinitcpio @@ -6,8 +6,10 @@ shopt -s extglob _list='--list' -_optcolor=1 _optverbose= +_optcolor=1 _optverbose= _optnoearly=1 _f_functions=functions +_f_skipcpio=./skipcpio +_tmp_files=() usage() { cat<<USAGE @@ -21,6 +23,7 @@ usage: ${0##*/} [action] [options] <initramfs> -x, --extract extract image to disk Options: + -e, --early display early cpio if possible -h, --help display this help -n, --nocolor disable colorized output -V, --version display version information @@ -131,7 +134,8 @@ analyze_image() { local kernver ratio columns=$(tput cols) image=$1 workdir=$(mktemp -d --tmpdir lsinitcpio.XXXXXX) - trap 'rm -rf "$workdir"' EXIT + _tmp_files+=("$workdir") + trap 'rm -rf "${_tmp_files[@]}"' EXIT # fallback in case tput failed us columns=${columns:-80} @@ -174,7 +178,7 @@ analyze_image() { explicitmod=($MODULES) # print results - imagename=$_image + imagename=$_origimage [[ -L $_image ]] && imagename+=" -> $(readlink -e "$_image")" msg 'Image: %s %s' "$imagename" [[ $version ]] && msg 'Created with mkinitcpio %s' "$version" @@ -225,10 +229,18 @@ analyze_image() { printf ' %s\n' $CLEANUPHOOKS printf '\n' fi + + msg 'Contains early CPIO:' + if (( _hasearly )); then + printf ' yes\n' + else + printf ' no\n' + fi + printf '\n' } -_opt_short='achlnVvx' -_opt_long=('analyze' 'config' 'help' 'list' 'nocolor' 'version' 'verbose' 'extract') +_opt_short='achlenVvx' +_opt_long=('analyze' 'config' 'help' 'list' 'early' 'nocolor' 'version' 'verbose' 'extract') parseopts "$_opt_short" "${_opt_long[@]}" -- "$@" || exit set -- "${OPTRET[@]}" @@ -245,6 +257,8 @@ while :; do exit 0 ;; -l|--list) _optlistcontents=1 ;; + -e|--early) + _optnoearly=0 ;; -n|--nocolor) _optcolor=0 ;; -V|--version) @@ -262,6 +276,8 @@ while :; do done _image=$1 +_origimage=$1 +_hasearly=0 if [[ -t 1 ]] && (( _optcolor )); then try_enable_color @@ -281,6 +297,15 @@ esac # read compression type _compress=$(detect_filetype "$_image") || exit +if (( _optnoearly )) && decomp "$_image" | bsdcpio -i --quiet --list | grep -q '^early_cpio'; then + _hasearly=1 + _tmp_image=$(mktemp --tmpdir lsinitcpio.XXXXXX) + _tmp_files+=("$_tmp_image") + trap 'rm -rf "${_tmp_files[@]}"' EXIT + $_f_skipcpio $_image > $_tmp_image + _image=$_tmp_image +fi + if (( _optanalyze )); then analyze_image "$_image" elif (( _optshowconfig )); then diff --git a/mkinitcpio b/mkinitcpio index 01fe8ba..cfe5747 100755 --- a/mkinitcpio +++ b/mkinitcpio @@ -221,10 +221,21 @@ build_image() { cpio_opts+=('-R' '0:0') fi + echo -n > "$out" + if [[ -d "$BUILDROOT/early_root" ]]; then + pushd "$BUILDROOT/early_root" >/dev/null + touch "$BUILDROOT/early_root/early_cpio" + find . -mindepth 1 -printf "%P\0" | + LANG=C bsdcpio "${cpio_opts[@]}" >> "$out" + popd >/dev/null + + rm -rf "$BUILDROOT/early_root" + fi + pushd "$BUILDROOT" >/dev/null find . -print0 | LANG=C bsdcpio "${cpio_opts[@]}" | - $compress $COMPRESSION_OPTIONS > "$out" + $compress $COMPRESSION_OPTIONS >> "$out" pipesave=("${PIPESTATUS[@]}") # save immediately popd >/dev/null diff --git a/skipcpio.c b/skipcpio.c new file mode 100644 index 0000000..445d7f6 --- /dev/null +++ b/skipcpio.c @@ -0,0 +1,123 @@ +/* dracut-install.c -- install files and executables + + Copyright (C) 2012 Harald Hoyer + Copyright (C) 2012 Red Hat, Inc. All rights reserved. + + This program is free software: you can redistribute it and/or modify + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; If not, see <http://www.gnu.org/licenses/>. +*/ + +#define PROGRAM_VERSION_STRING "1" + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> + +#define CPIO_END "TRAILER!!!" +#define CPIO_ENDLEN (sizeof(CPIO_END)-1) + +static char buf[CPIO_ENDLEN * 2 + 1]; + +int main(int argc, char **argv) +{ + FILE *f; + size_t s; + + if (argc != 2) { + fprintf(stderr, "Usage: %s <file>\n", argv[0]); + exit(1); + } + + f = fopen(argv[1], "r"); + + if (f == NULL) { + fprintf(stderr, "Cannot open file '%s'\n", argv[1]); + exit(1); + } + + s = fread(buf, 6, 1, f); + if (s <= 0) { + fprintf(stderr, "Read error from file '%s'\n", argv[1]); + fclose(f); + exit(1); + } + fseek(f, 0, SEEK_SET); + + /* check, if this is a cpio archive */ + if ((buf[0] == 0x71 && buf[1] == 0xc7) + || (buf[0] == '0' && buf[1] == '7' && buf[2] == '0' && buf[3] == '7' && buf[4] == '0' && buf[5] == '1')) { + long pos = 0; + + /* Search for CPIO_END */ + do { + char *h; + fseek(f, pos, SEEK_SET); + buf[sizeof(buf) - 1] = 0; + s = fread(buf, CPIO_ENDLEN, 2, f); + if (s <= 0) + break; + + h = strstr(buf, CPIO_END); + if (h) { + pos = (h - buf) + pos + CPIO_ENDLEN; + fseek(f, pos, SEEK_SET); + break; + } + pos += CPIO_ENDLEN; + } while (!feof(f)); + + if (feof(f)) { + /* CPIO_END not found, just cat the whole file */ + fseek(f, 0, SEEK_SET); + } else { + /* skip zeros */ + while (!feof(f)) { + size_t i; + + buf[sizeof(buf) - 1] = 0; + s = fread(buf, 1, sizeof(buf) - 1, f); + if (s <= 0) + break; + + for (i = 0; (i < s) && (buf[i] == 0); i++) ; + + if (buf[i] != 0) { + pos += i; + fseek(f, pos, SEEK_SET); + break; + } + + pos += s; + } + } + } + /* cat out the rest */ + while (!feof(f)) { + s = fread(buf, 1, sizeof(buf), f); + if (s <= 0) + break; + + s = fwrite(buf, 1, s, stdout); + if (s <= 0) + break; + } + fclose(f); + + return EXIT_SUCCESS; +} -- 2.0.2
On Sun, Nov 02, 2014 at 02:18:00AM -0500, Weng Xuetian wrote:
Add early microcode support with early cpio. All files added under /early_root will be created as a separate cpio prepend to original one. Add a early_cpio file to early cpio to detect whether cpio contains early cpio, thus we can have lsinitcpio work normally like old one. ---
Hi, I'm not opposed to this idea, but this patch has a long way to go before it's in mergeable shape.
.gitignore | 1 + Makefile | 9 +++- install/microcode | 24 +++++++++++ lsinitcpio | 35 +++++++++++++--- mkinitcpio | 13 +++++- skipcpio.c | 123 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 198 insertions(+), 7 deletions(-) create mode 100644 install/microcode create mode 100644 skipcpio.c
diff --git a/.gitignore b/.gitignore index a814a0f..1e501f0 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ lsinitcpio.1 *~ *.bak mkinitcpio-*.tar.gz +skipcpio diff --git a/Makefile b/Makefile index 5af0eb2..a3a3591 100644 --- a/Makefile +++ b/Makefile @@ -20,7 +20,10 @@ DIRS = \ /usr/lib/systemd/system/shutdown.target.wants \ /usr/lib/tmpfiles.d
-all: doc +all: doc skipcpio + +skipcpio: skipcpio.c + $(CC) skipcpio.c -o skipcpio
Get rid of this compile command. You just want the dependency without the build rule. Make's default rules for generating binaries from .c are better than yours, as they'll add in CFLAGS and LDFLAGS from the environment.
MANPAGES = \ man/mkinitcpio.8 \ @@ -40,6 +43,7 @@ install: all < mkinitcpio > $(DESTDIR)/usr/bin/mkinitcpio
sed -e 's|\(^_f_functions\)=.*|\1=/usr/lib/initcpio/functions|' \ + -e 's|\(^_f_skipcpio\)=.*|\1=/usr/lib/initcpio/skipcpio|' \ -e 's|%VERSION%|$(VERSION)|g' \ < lsinitcpio > $(DESTDIR)/usr/bin/lsinitcpio
@@ -67,6 +71,8 @@ install: all ln -s mkinitcpio $(DESTDIR)/usr/share/bash-completion/completions/lsinitcpio install -m644 shell/zsh-completion $(DESTDIR)/usr/share/zsh/site-functions/_mkinitcpio
+ install -m755 skipcpio $(DESTDIR)/usr/lib/initcpio/skipcpio + doc: $(MANPAGES) man/%: man/%.txt Makefile a2x -d manpage \ @@ -76,6 +82,7 @@ man/%: man/%.txt Makefile
clean: $(RM) mkinitcpio-${VERSION}.tar.gz $(MANPAGES) + $(RM) skipcpio
dist: doc echo $(VERSION) > VERSION diff --git a/install/microcode b/install/microcode new file mode 100644 index 0000000..4dfd69e --- /dev/null +++ b/install/microcode @@ -0,0 +1,24 @@ +#!/bin/bash + +build() { + ucode_dir=(amd-ucode intel-ucode) + ucode_dest=(AuthenticAMD.bin GenuineIntel.bin) + + for idx in 0 1; do + fw=${ucode_dir[$idx]} + for fwpath in "${_d_firmware[@]}"; do + if [[ -d $fwpath/$fw ]]; then + add_dir '/early_root/kernel/x86/microcode' + cat $fwpath/$fw/* > $BUILDROOT/early_root/kernel/x86/microcode/${ucode_dest[$idx]}
I don't understand this loop. It adds both AMD and Intel firmware to the image? Concatenates multiple files? Where do these files even come from? I've found: $ pkgfile -vr amd-ucode/ core/linux-firmware 20141009.0e5f637-1 /usr/lib/firmware/amd-ucode/microcode_amd.bin core/linux-firmware 20141009.0e5f637-1 /usr/lib/firmware/amd-ucode/microcode_amd.bin.asc core/linux-firmware 20141009.0e5f637-1 /usr/lib/firmware/amd-ucode/microcode_amd_fam15h.bin core/linux-firmware 20141009.0e5f637-1 /usr/lib/firmware/amd-ucode/microcode_amd_fam15h.bin.asc We really need all of this? Even the GPG signatures? What about Intel? $ pkgfile -vr intel-ucode/ extra/intel-ucode 20140913-1 /usr/share/licenses/intel-ucode/LICENSE Doesn't look too promising... I think you've missed a step somewhere.
+ fi + done + done +} + +help() { + cat <<HELPEOF +This hook generate early ucode update +HELPEOF +} + +# vim: set ft=sh ts=4 sw=4 et: diff --git a/lsinitcpio b/lsinitcpio index 9567b79..902bfe2 100755 --- a/lsinitcpio +++ b/lsinitcpio @@ -6,8 +6,10 @@ shopt -s extglob
_list='--list' -_optcolor=1 _optverbose= +_optcolor=1 _optverbose= _optnoearly=1 _f_functions=functions +_f_skipcpio=./skipcpio +_tmp_files=()
usage() { cat<<USAGE @@ -21,6 +23,7 @@ usage: ${0##*/} [action] [options] <initramfs> -x, --extract extract image to disk
Options: + -e, --early display early cpio if possible -h, --help display this help -n, --nocolor disable colorized output -V, --version display version information @@ -131,7 +134,8 @@ analyze_image() { local kernver ratio columns=$(tput cols) image=$1
workdir=$(mktemp -d --tmpdir lsinitcpio.XXXXXX) - trap 'rm -rf "$workdir"' EXIT + _tmp_files+=("$workdir") + trap 'rm -rf "${_tmp_files[@]}"' EXIT
# fallback in case tput failed us columns=${columns:-80} @@ -174,7 +178,7 @@ analyze_image() { explicitmod=($MODULES)
# print results - imagename=$_image + imagename=$_origimage [[ -L $_image ]] && imagename+=" -> $(readlink -e "$_image")" msg 'Image: %s %s' "$imagename" [[ $version ]] && msg 'Created with mkinitcpio %s' "$version" @@ -225,10 +229,18 @@ analyze_image() { printf ' %s\n' $CLEANUPHOOKS printf '\n' fi + + msg 'Contains early CPIO:' + if (( _hasearly )); then + printf ' yes\n' + else + printf ' no\n' + fi + printf '\n' }
-_opt_short='achlnVvx' -_opt_long=('analyze' 'config' 'help' 'list' 'nocolor' 'version' 'verbose' 'extract') +_opt_short='achlenVvx' +_opt_long=('analyze' 'config' 'help' 'list' 'early' 'nocolor' 'version' 'verbose' 'extract')
parseopts "$_opt_short" "${_opt_long[@]}" -- "$@" || exit set -- "${OPTRET[@]}" @@ -245,6 +257,8 @@ while :; do exit 0 ;; -l|--list) _optlistcontents=1 ;; + -e|--early) + _optnoearly=0 ;;
This flag is really confusing. The name of the flag is "early", but passing it seems to turn detection of the early initramfs image OFF (contrary to the description given by the usage). Why can't we avoid the flag and just do the right thing all the time? No one's going to care about extracting or listing the early firmware. If they really do, they can just use bsdtar/bsdcpio directly on the image.
-n|--nocolor) _optcolor=0 ;; -V|--version) @@ -262,6 +276,8 @@ while :; do done
_image=$1 +_origimage=$1 +_hasearly=0
if [[ -t 1 ]] && (( _optcolor )); then try_enable_color @@ -281,6 +297,15 @@ esac # read compression type _compress=$(detect_filetype "$_image") || exit
Because you leave this alone, an initramfs with firmware prepended will now always show up as uncompressed, regardless of any compression used in the real image.
+if (( _optnoearly )) && decomp "$_image" | bsdcpio -i --quiet --list | grep -q '^early_cpio'; then
This would be just as effective if you checked the very firsy entry in "$_image" for "/kernel/x86/microcode/...". As the first file in an initramfs, this will fail to extract properly (as the parent paths don't exist). So, there's a high probability that we're finding some early firmware. Bonus: no need for the ugly-ass flag file. Additional bonus: since you'll have the path (after matching it), you can grab the name of the firmware and display it in -a output.
+ _hasearly=1 + _tmp_image=$(mktemp --tmpdir lsinitcpio.XXXXXX) + _tmp_files+=("$_tmp_image")
This is no "temporary" image. This is the *real* initramfs.
+ trap 'rm -rf "${_tmp_files[@]}"' EXIT
You also set this trap in analyze_image. It's a very generic thing that you're doing -- just move it to a common path and call it once.
+ $_f_skipcpio $_image > $_tmp_image
Needs more quoting...
+ _image=$_tmp_image +fi + if (( _optanalyze )); then analyze_image "$_image" elif (( _optshowconfig )); then diff --git a/mkinitcpio b/mkinitcpio index 01fe8ba..cfe5747 100755 --- a/mkinitcpio +++ b/mkinitcpio @@ -221,10 +221,21 @@ build_image() { cpio_opts+=('-R' '0:0') fi
+ echo -n > "$out"
If your goal is to truncate "$out", then all you need to do is:
"$out"
+ if [[ -d "$BUILDROOT/early_root" ]]; then + pushd "$BUILDROOT/early_root" >/dev/null + touch "$BUILDROOT/early_root/early_cpio" + find . -mindepth 1 -printf "%P\0" | + LANG=C bsdcpio "${cpio_opts[@]}" >> "$out"
No error checking?
+ popd >/dev/null + + rm -rf "$BUILDROOT/early_root"
Removing this directory now makes debugging artificially difficult. Do we really need to invoke bsdcpio here? It's a single uncompressed file. We could just write the CPIO header ourselves as a preamble with printf...
+ fi + pushd "$BUILDROOT" >/dev/null find . -print0 | LANG=C bsdcpio "${cpio_opts[@]}" | - $compress $COMPRESSION_OPTIONS > "$out" + $compress $COMPRESSION_OPTIONS >> "$out" pipesave=("${PIPESTATUS[@]}") # save immediately popd >/dev/null
diff --git a/skipcpio.c b/skipcpio.c new file mode 100644 index 0000000..445d7f6 --- /dev/null +++ b/skipcpio.c
I really dislike the idea of adding architecture specific code to mkinitcpio. I've resisted it several times. I need a really good reason to add someone else's unruly C to mkinitcpio...
@@ -0,0 +1,123 @@ +/* dracut-install.c -- install files and executables + + Copyright (C) 2012 Harald Hoyer + Copyright (C) 2012 Red Hat, Inc. All rights reserved. + + This program is free software: you can redistribute it and/or modify + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; If not, see <http://www.gnu.org/licenses/>. +*/ + +#define PROGRAM_VERSION_STRING "1" + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> + +#define CPIO_END "TRAILER!!!" +#define CPIO_ENDLEN (sizeof(CPIO_END)-1) + +static char buf[CPIO_ENDLEN * 2 + 1]; + +int main(int argc, char **argv) +{ + FILE *f; + size_t s; + + if (argc != 2) { + fprintf(stderr, "Usage: %s <file>\n", argv[0]); + exit(1); + } + + f = fopen(argv[1], "r"); + + if (f == NULL) { + fprintf(stderr, "Cannot open file '%s'\n", argv[1]); + exit(1); + } + + s = fread(buf, 6, 1, f); + if (s <= 0) { + fprintf(stderr, "Read error from file '%s'\n", argv[1]); + fclose(f); + exit(1); + } + fseek(f, 0, SEEK_SET); + + /* check, if this is a cpio archive */ + if ((buf[0] == 0x71 && buf[1] == 0xc7) + || (buf[0] == '0' && buf[1] == '7' && buf[2] == '0' && buf[3] == '7' && buf[4] == '0' && buf[5] == '1')) { + long pos = 0; + + /* Search for CPIO_END */ + do { + char *h; + fseek(f, pos, SEEK_SET); + buf[sizeof(buf) - 1] = 0; + s = fread(buf, CPIO_ENDLEN, 2, f); + if (s <= 0) + break; + + h = strstr(buf, CPIO_END); + if (h) { + pos = (h - buf) + pos + CPIO_ENDLEN; + fseek(f, pos, SEEK_SET); + break; + } + pos += CPIO_ENDLEN; + } while (!feof(f)); + + if (feof(f)) { + /* CPIO_END not found, just cat the whole file */ + fseek(f, 0, SEEK_SET); + } else { + /* skip zeros */ + while (!feof(f)) { + size_t i; + + buf[sizeof(buf) - 1] = 0; + s = fread(buf, 1, sizeof(buf) - 1, f); + if (s <= 0) + break; + + for (i = 0; (i < s) && (buf[i] == 0); i++) ; + + if (buf[i] != 0) { + pos += i; + fseek(f, pos, SEEK_SET); + break; + } + + pos += s; + } + } + } + /* cat out the rest */ + while (!feof(f)) { + s = fread(buf, 1, sizeof(buf), f); + if (s <= 0) + break; + + s = fwrite(buf, 1, s, stdout); + if (s <= 0) + break; + } + fclose(f); + + return EXIT_SUCCESS; +} -- 2.0.2
On Monday 03 November 2014 08:44:16, Dave Reisner Wrote:
On Sun, Nov 02, 2014 at 02:18:00AM -0500, Weng Xuetian wrote:
Add early microcode support with early cpio. All files added under /early_root will be created as a separate cpio prepend to original one. Add a early_cpio file to early cpio to detect whether cpio contains early cpio, thus we can have lsinitcpio work normally like old one. ---
Hi, I'm not opposed to this idea, but this patch has a long way to go before it's in mergeable shape.
.gitignore | 1 + Makefile | 9 +++- install/microcode | 24 +++++++++++ lsinitcpio | 35 +++++++++++++--- mkinitcpio | 13 +++++- skipcpio.c | 123 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 198 insertions(+), 7 deletions(-) create mode 100644 install/microcode create mode 100644 skipcpio.c
diff --git a/.gitignore b/.gitignore index a814a0f..1e501f0 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ lsinitcpio.1
*~ *.bak mkinitcpio-*.tar.gz
+skipcpio diff --git a/Makefile b/Makefile index 5af0eb2..a3a3591 100644 --- a/Makefile +++ b/Makefile @@ -20,7 +20,10 @@ DIRS = \
/usr/lib/systemd/system/shutdown.target.wants \ /usr/lib/tmpfiles.d
-all: doc +all: doc skipcpio + +skipcpio: skipcpio.c + $(CC) skipcpio.c -o skipcpio
Get rid of this compile command. You just want the dependency without the build rule. Make's default rules for generating binaries from .c are better than yours, as they'll add in CFLAGS and LDFLAGS from the environment.
MANPAGES = \
man/mkinitcpio.8 \
@@ -40,6 +43,7 @@ install: all
< mkinitcpio > $(DESTDIR)/usr/bin/mkinitcpio
sed -e 's|\(^_f_functions\)=.*|\1=/usr/lib/initcpio/functions|' \
+ -e 's|\(^_f_skipcpio\)=.*|\1=/usr/lib/initcpio/skipcpio|' \
-e 's|%VERSION%|$(VERSION)|g' \ < lsinitcpio > $(DESTDIR)/usr/bin/lsinitcpio
@@ -67,6 +71,8 @@ install: all
ln -s mkinitcpio $(DESTDIR)/usr/share/bash-completion/completions/lsinitcpio install -m644 shell/zsh-completion $(DESTDIR)/usr/share/zsh/site-functions/_mkinitcpio> + install -m755 skipcpio $(DESTDIR)/usr/lib/initcpio/skipcpio +
doc: $(MANPAGES) man/%: man/%.txt Makefile
a2x -d manpage \
@@ -76,6 +82,7 @@ man/%: man/%.txt Makefile
clean: $(RM) mkinitcpio-${VERSION}.tar.gz $(MANPAGES)
+ $(RM) skipcpio
dist: doc
echo $(VERSION) > VERSION
diff --git a/install/microcode b/install/microcode new file mode 100644 index 0000000..4dfd69e --- /dev/null +++ b/install/microcode @@ -0,0 +1,24 @@ +#!/bin/bash + +build() { + ucode_dir=(amd-ucode intel-ucode) + ucode_dest=(AuthenticAMD.bin GenuineIntel.bin) + + for idx in 0 1; do + fw=${ucode_dir[$idx]} + for fwpath in "${_d_firmware[@]}"; do + if [[ -d $fwpath/$fw ]]; then + add_dir '/early_root/kernel/x86/microcode' + cat $fwpath/$fw/* > $BUILDROOT/early_root/kernel/x86/microcode/${ucode_dest[$idx]} I don't understand this loop. It adds both AMD and Intel firmware to the image? Concatenates multiple files? Where do these files even come from? I've found: The intel-ucode you need is the old version, which put all intel-ucode file into /usr/lib/firmware/intel-ucode, microcode_ctl can be used. I may check how fedora package amd-ucode files.
The implementation idea comes from dracut, because I'm totally opposed to the current intel-ucode.img way to do it. Well, if mkinitcpio must depends on how arch (I'm not a arch user) packages intel-ucode file, it can be changed. But somehow generating early_cpio is useful not only for amd/intel ucode update, but also some acpi update.
$ pkgfile -vr amd-ucode/ core/linux-firmware 20141009.0e5f637-1 /usr/lib/firmware/amd-ucode/microcode_amd.bin core/linux-firmware 20141009.0e5f637-1 /usr/lib/firmware/amd-ucode/microcode_amd.bin.asc core/linux-firmware 20141009.0e5f637-1 /usr/lib/firmware/amd-ucode/microcode_amd_fam15h.bin core/linux-firmware 20141009.0e5f637-1 /usr/lib/firmware/amd-ucode/microcode_amd_fam15h.bin.asc
We really need all of this? Even the GPG signatures? What about Intel?
$ pkgfile -vr intel-ucode/ extra/intel-ucode 20140913-1 /usr/share/licenses/intel-ucode/LICENSE
Doesn't look too promising... I think you've missed a step somewhere.
+ fi + done + done +} + +help() { + cat <<HELPEOF +This hook generate early ucode update +HELPEOF +} + +# vim: set ft=sh ts=4 sw=4 et: diff --git a/lsinitcpio b/lsinitcpio index 9567b79..902bfe2 100755 --- a/lsinitcpio +++ b/lsinitcpio @@ -6,8 +6,10 @@
shopt -s extglob
_list='--list'
-_optcolor=1 _optverbose= +_optcolor=1 _optverbose= _optnoearly=1
_f_functions=functions
+_f_skipcpio=./skipcpio +_tmp_files=()
usage() {
cat<<USAGE
@@ -21,6 +23,7 @@ usage: ${0##*/} [action] [options] <initramfs>
-x, --extract extract image to disk
Options: + -e, --early display early cpio if possible
-h, --help display this help -n, --nocolor disable colorized output -V, --version display version information
@@ -131,7 +134,8 @@ analyze_image() {
local kernver ratio columns=$(tput cols) image=$1
workdir=$(mktemp -d --tmpdir lsinitcpio.XXXXXX)
- trap 'rm -rf "$workdir"' EXIT + _tmp_files+=("$workdir") + trap 'rm -rf "${_tmp_files[@]}"' EXIT
# fallback in case tput failed us columns=${columns:-80}
@@ -174,7 +178,7 @@ analyze_image() {
explicitmod=($MODULES)
# print results
- imagename=$_image + imagename=$_origimage
[[ -L $_image ]] && imagename+=" -> $(readlink -e "$_image")" msg 'Image: %s %s' "$imagename" [[ $version ]] && msg 'Created with mkinitcpio %s' "$version"
@@ -225,10 +229,18 @@ analyze_image() {
printf ' %s\n' $CLEANUPHOOKS printf '\n'
fi
+ + msg 'Contains early CPIO:' + if (( _hasearly )); then + printf ' yes\n' + else + printf ' no\n' + fi + printf '\n'
}
-_opt_short='achlnVvx' -_opt_long=('analyze' 'config' 'help' 'list' 'nocolor' 'version' 'verbose' 'extract') +_opt_short='achlenVvx' +_opt_long=('analyze' 'config' 'help' 'list' 'early' 'nocolor' 'version' 'verbose' 'extract')> parseopts "$_opt_short" "${_opt_long[@]}" -- "$@" || exit set -- "${OPTRET[@]}"
@@ -245,6 +257,8 @@ while :; do
exit 0 ;;
-l|--list)
_optlistcontents=1 ;;
+ -e|--early) + _optnoearly=0 ;;
This flag is really confusing. The name of the flag is "early", but passing it seems to turn detection of the early initramfs image OFF (contrary to the description given by the usage). Why can't we avoid the flag and just do the right thing all the time? No one's going to care about extracting or listing the early firmware. If they really do, they can just use bsdtar/bsdcpio directly on the image.
Well, personally I don't know which idea is better, but maybe merge all cpio file together? It's also useful to check whether merged cpio file contains correct files. There are some way to do this: 1. Having no ability to list the early cpio. - well, if call bsdcpio is what we want. 2. List them together in the same output - do we need a header for each part? this might break compatibility for people who use lsinitcpio in script 3. current way.
-n|--nocolor)
_optcolor=0 ;;
-V|--version)
@@ -262,6 +276,8 @@ while :; do
done
_image=$1
+_origimage=$1 +_hasearly=0
if [[ -t 1 ]] && (( _optcolor )); then
try_enable_color
@@ -281,6 +297,15 @@ esac
# read compression type _compress=$(detect_filetype "$_image") || exit
Because you leave this alone, an initramfs with firmware prepended will now always show up as uncompressed, regardless of any compression used in the real image.
+if (( _optnoearly )) && decomp "$_image" | bsdcpio -i --quiet --list | grep -q '^early_cpio'; then This would be just as effective if you checked the very firsy entry in "$_image" for "/kernel/x86/microcode/...". As the first file in an initramfs, this will fail to extract properly (as the parent paths don't exist). So, there's a high probability that we're finding some early firmware. Bonus: no need for the ugly-ass flag file. Additional bonus: since you'll have the path (after matching it), you can grab the name of the firmware and display it in -a output.
Nope, early_cpio can be also used in some other way. Though currently we only support microcode, but we might also want to add acpi override: http://git.kernel.org/cgit/boot/dracut/dracut.git/tree/dracut.sh#n1562
+ _hasearly=1 + _tmp_image=$(mktemp --tmpdir lsinitcpio.XXXXXX) + _tmp_files+=("$_tmp_image")
This is no "temporary" image. This is the *real* initramfs.
+ trap 'rm -rf "${_tmp_files[@]}"' EXIT
You also set this trap in analyze_image. It's a very generic thing that you're doing -- just move it to a common path and call it once.
+ $_f_skipcpio $_image > $_tmp_image
Needs more quoting...
+ _image=$_tmp_image +fi +
if (( _optanalyze )); then
analyze_image "$_image"
elif (( _optshowconfig )); then
diff --git a/mkinitcpio b/mkinitcpio index 01fe8ba..cfe5747 100755 --- a/mkinitcpio +++ b/mkinitcpio @@ -221,10 +221,21 @@ build_image() {
cpio_opts+=('-R' '0:0')
fi
+ echo -n > "$out"
If your goal is to truncate "$out", then all you need to do is:
"$out"
+ if [[ -d "$BUILDROOT/early_root" ]]; then + pushd "$BUILDROOT/early_root" >/dev/null + touch "$BUILDROOT/early_root/early_cpio" + find . -mindepth 1 -printf "%P\0" | + LANG=C bsdcpio "${cpio_opts[@]}" >> "$out"
No error checking?
+ popd >/dev/null + + rm -rf "$BUILDROOT/early_root"
Removing this directory now makes debugging artificially difficult. Do we really need to invoke bsdcpio here? It's a single uncompressed file. We could just write the CPIO header ourselves as a preamble with printf...
Initially I'd like to use a separate directory in buildroot, but later found that might not work well with existing functions so I tried to reuse the functions but make a convention that early_root is used for separate thing. There can be multiple files in different directory, I didn't make the assumption that this feature can only be used for microcode update.
+ fi +
pushd "$BUILDROOT" >/dev/null find . -print0 |
LANG=C bsdcpio "${cpio_opts[@]}" |
- $compress $COMPRESSION_OPTIONS > "$out" + $compress $COMPRESSION_OPTIONS >> "$out"
pipesave=("${PIPESTATUS[@]}") # save immediately popd >/dev/null
diff --git a/skipcpio.c b/skipcpio.c new file mode 100644 index 0000000..445d7f6 --- /dev/null +++ b/skipcpio.c
I really dislike the idea of adding architecture specific code to mkinitcpio. I've resisted it several times. I need a really good reason to add someone else's unruly C to mkinitcpio...
Well, I use C here because there is some existing code to reuse, and I did aware mkinitcpio has no C code, the implementation of this C is simple so we can reimplement is with shell script.
@@ -0,0 +1,123 @@ +/* dracut-install.c -- install files and executables + + Copyright (C) 2012 Harald Hoyer + Copyright (C) 2012 Red Hat, Inc. All rights reserved. + + This program is free software: you can redistribute it and/or modify + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; If not, see <http://www.gnu.org/licenses/>. +*/ + +#define PROGRAM_VERSION_STRING "1" + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> + +#define CPIO_END "TRAILER!!!" +#define CPIO_ENDLEN (sizeof(CPIO_END)-1) + +static char buf[CPIO_ENDLEN * 2 + 1]; + +int main(int argc, char **argv) +{ + FILE *f; + size_t s; + + if (argc != 2) { + fprintf(stderr, "Usage: %s <file>\n", argv[0]); + exit(1); + } + + f = fopen(argv[1], "r"); + + if (f == NULL) { + fprintf(stderr, "Cannot open file '%s'\n", argv[1]); + exit(1); + } + + s = fread(buf, 6, 1, f); + if (s <= 0) { + fprintf(stderr, "Read error from file '%s'\n", argv[1]); + fclose(f); + exit(1); + } + fseek(f, 0, SEEK_SET); + + /* check, if this is a cpio archive */ + if ((buf[0] == 0x71 && buf[1] == 0xc7) + || (buf[0] == '0' && buf[1] == '7' && buf[2] == '0' && buf[3] == '7' && buf[4] == '0' && buf[5] == '1')) { + long pos = 0; + + /* Search for CPIO_END */ + do { + char *h; + fseek(f, pos, SEEK_SET); + buf[sizeof(buf) - 1] = 0; + s = fread(buf, CPIO_ENDLEN, 2, f); + if (s <= 0) + break; + + h = strstr(buf, CPIO_END); + if (h) { + pos = (h - buf) + pos + CPIO_ENDLEN; + fseek(f, pos, SEEK_SET); + break; + } + pos += CPIO_ENDLEN; + } while (!feof(f)); + + if (feof(f)) { + /* CPIO_END not found, just cat the whole file */ + fseek(f, 0, SEEK_SET); + } else { + /* skip zeros */ + while (!feof(f)) { + size_t i; + + buf[sizeof(buf) - 1] = 0; + s = fread(buf, 1, sizeof(buf) - 1, f); + if (s <= 0) + break; + + for (i = 0; (i < s) && (buf[i] == 0); i++) ; + + if (buf[i] != 0) { + pos += i; + fseek(f, pos, SEEK_SET); + break; + } + + pos += s; + } + } + } + /* cat out the rest */ + while (!feof(f)) { + s = fread(buf, 1, sizeof(buf), f); + if (s <= 0) + break; + + s = fwrite(buf, 1, s, stdout); + if (s <= 0) + break; + } + fclose(f); + + return EXIT_SUCCESS; +}
On Mon, Nov 03, 2014 at 10:00:41AM -0500, Weng Xuetian wrote:
Nope, early_cpio can be also used in some other way. Though currently we only support microcode, but we might also want to add acpi override: http://git.kernel.org/cgit/boot/dracut/dracut.git/tree/dracut.sh#n1562
That's fine, but then you need to structure your patch to not add a hook called "microcode", but to actually implement proper support for an early CPIO image prepended to the image. Otherwise, any restrictions which I'm pointing out are purely self-inflicted due to the expressed intent of your patch.
On Monday 03 November 2014 11:21:03, Dave Reisner Wrote:
On Mon, Nov 03, 2014 at 10:00:41AM -0500, Weng Xuetian wrote:
Nope, early_cpio can be also used in some other way. Though currently we only support microcode, but we might also want to add acpi override: http://git.kernel.org/cgit/boot/dracut/dracut.git/tree/dracut.sh#n1562
That's fine, but then you need to structure your patch to not add a hook called "microcode", but to actually implement proper support for an early CPIO image prepended to the image. Otherwise, any restrictions which I'm pointing out are purely self-inflicted due to the expressed intent of your patch.
The point is the early image file doesn't exist anyway but need to be built from scratch. The firmware might not be installed, might not be even needed if there is an automatic detection in the procedure of build image. Using a hook (name might be other than "microcode") can keep enough flexibilty to do this.
Add early microcode support with early cpio. All files added under /early_root will be created as a separate cpio prepend to original one. Add a early_cpio file to early cpio to detect whether cpio contains early cpio, thus we can have lsinitcpio work normally like old one. --- Makefile | 7 ++++++- install/microcode | 24 ++++++++++++++++++++++++ lsinitcpio | 29 +++++++++++++++++++++++++++-- mkinitcpio | 43 ++++++++++++++++++++++++++++++------------- skipcpio | 31 +++++++++++++++++++++++++++++++ 5 files changed, 118 insertions(+), 16 deletions(-) create mode 100644 install/microcode create mode 100755 skipcpio diff --git a/Makefile b/Makefile index 5af0eb2..83a21dc 100644 --- a/Makefile +++ b/Makefile @@ -40,10 +40,15 @@ install: all < mkinitcpio > $(DESTDIR)/usr/bin/mkinitcpio sed -e 's|\(^_f_functions\)=.*|\1=/usr/lib/initcpio/functions|' \ + -e 's|\(^_f_skipcpio\)=.*|\1=/usr/lib/initcpio/skipcpio|' \ -e 's|%VERSION%|$(VERSION)|g' \ < lsinitcpio > $(DESTDIR)/usr/bin/lsinitcpio - chmod 755 $(DESTDIR)/usr/bin/lsinitcpio $(DESTDIR)/usr/bin/mkinitcpio + sed -e 's|\(^_f_functions\)=.*|\1=/usr/lib/initcpio/functions|' \ + < skipcpio > $(DESTDIR)/usr/lib/initcpio/skipcpio + + chmod 755 $(DESTDIR)/usr/bin/lsinitcpio $(DESTDIR)/usr/bin/mkinitcpio \ + $(DESTDIR)/usr/lib/initcpio/skipcpio install -m644 mkinitcpio.conf $(DESTDIR)/etc/mkinitcpio.conf install -m755 -t $(DESTDIR)/usr/lib/initcpio init shutdown diff --git a/install/microcode b/install/microcode new file mode 100644 index 0000000..4dfd69e --- /dev/null +++ b/install/microcode @@ -0,0 +1,24 @@ +#!/bin/bash + +build() { + ucode_dir=(amd-ucode intel-ucode) + ucode_dest=(AuthenticAMD.bin GenuineIntel.bin) + + for idx in 0 1; do + fw=${ucode_dir[$idx]} + for fwpath in "${_d_firmware[@]}"; do + if [[ -d $fwpath/$fw ]]; then + add_dir '/early_root/kernel/x86/microcode' + cat $fwpath/$fw/* > $BUILDROOT/early_root/kernel/x86/microcode/${ucode_dest[$idx]} + fi + done + done +} + +help() { + cat <<HELPEOF +This hook generate early ucode update +HELPEOF +} + +# vim: set ft=sh ts=4 sw=4 et: diff --git a/lsinitcpio b/lsinitcpio index 9567b79..8d0ed1f 100755 --- a/lsinitcpio +++ b/lsinitcpio @@ -8,6 +8,8 @@ shopt -s extglob _list='--list' _optcolor=1 _optverbose= _f_functions=functions +_f_skipcpio=./skipcpio +_tmp_files=() usage() { cat<<USAGE @@ -131,7 +133,7 @@ analyze_image() { local kernver ratio columns=$(tput cols) image=$1 workdir=$(mktemp -d --tmpdir lsinitcpio.XXXXXX) - trap 'rm -rf "$workdir"' EXIT + _tmp_files+=("$workdir") # fallback in case tput failed us columns=${columns:-80} @@ -174,7 +176,7 @@ analyze_image() { explicitmod=($MODULES) # print results - imagename=$_image + imagename=$_origimage [[ -L $_image ]] && imagename+=" -> $(readlink -e "$_image")" msg 'Image: %s %s' "$imagename" [[ $version ]] && msg 'Created with mkinitcpio %s' "$version" @@ -225,6 +227,14 @@ analyze_image() { printf ' %s\n' $CLEANUPHOOKS printf '\n' fi + + msg 'Contains early CPIO:' + if (( _hasearly )); then + printf ' yes\n' + else + printf ' no\n' + fi + printf '\n' } _opt_short='achlnVvx' @@ -262,6 +272,8 @@ while :; do done _image=$1 +_origimage=$1 +_hasearly=0 if [[ -t 1 ]] && (( _optcolor )); then try_enable_color @@ -278,9 +290,22 @@ case $(( _optanalyze + _optlistcontents + _optshowconfig )) in die "Only one action may be specified at a time" ;; esac +trap '(( ${#_tmp_files[@]} )) && rm -rf "${_tmp_files[@]}"' EXIT + # read compression type _compress=$(detect_filetype "$_image") || exit +if decomp "$_image" | bsdcpio -i --quiet --list | grep -q '^early_cpio'; then + _hasearly=1 + _real_image=$(mktemp --tmpdir lsinitcpio.XXXXXX) + _tmp_files+=("$_real_image") + "$_f_skipcpio" "$_image" > "$_real_image" + _image="$_real_image" + # read compression type again for real image + _compress=$(detect_filetype "$_image") || exit +fi + + if (( _optanalyze )); then analyze_image "$_image" elif (( _optshowconfig )); then diff --git a/mkinitcpio b/mkinitcpio index 01fe8ba..d5dd46a 100755 --- a/mkinitcpio +++ b/mkinitcpio @@ -221,19 +221,36 @@ build_image() { cpio_opts+=('-R' '0:0') fi - pushd "$BUILDROOT" >/dev/null - find . -print0 | - LANG=C bsdcpio "${cpio_opts[@]}" | - $compress $COMPRESSION_OPTIONS > "$out" - pipesave=("${PIPESTATUS[@]}") # save immediately - popd >/dev/null - - if (( pipesave[0] )); then - errmsg="find reported an error" - elif (( pipesave[1] )); then - errmsg="bsdcpio reported an error" - elif (( pipesave[2] )); then - errmsg="$compress reported an error" + > "$out" + if [[ -d "$BUILDROOT/early_root" ]]; then + pushd "$BUILDROOT/early_root" >/dev/null + touch "$BUILDROOT/early_root/early_cpio" + find . -mindepth 1 -printf "%P\0" | + LANG=C bsdcpio "${cpio_opts[@]}" >> "$out" + popd >/dev/null + pipesave=("${PIPESTATUS[@]}") # save immediately + if (( pipesave[0] )); then + errmsg="find reported an error" + elif (( pipesave[1] )); then + errmsg="bsdcpio reported an error" + fi + fi + + if [[ ! $errmsg ]]; then + pushd "$BUILDROOT" >/dev/null + find . -path "./early_root" -prune -o -print0 | + LANG=C bsdcpio "${cpio_opts[@]}" | + $compress $COMPRESSION_OPTIONS >> "$out" + pipesave=("${PIPESTATUS[@]}") # save immediately + popd >/dev/null + + if (( pipesave[0] )); then + errmsg="find reported an error" + elif (( pipesave[1] )); then + errmsg="bsdcpio reported an error" + elif (( pipesave[2] )); then + errmsg="$compress reported an error" + fi fi if (( _builderrors )); then diff --git a/skipcpio b/skipcpio new file mode 100755 index 0000000..b106d87 --- /dev/null +++ b/skipcpio @@ -0,0 +1,31 @@ +#!/bin/bash +# +# skipcpio - skip current uncompressed cpio part +# + +shopt -s extglob + +_f_functions=functions + +. "$_f_functions" + +die() { + error "$@" + exit 1 +} + +_image=$1 +[[ $_image ]] || die "No image specified" +[[ -f $_image ]] || die "No such file: %s" "$_image" + +_CPIO_END='TRAILER!!!' + +# check if it's a cpio file +if [[ $(hexdump -n 6 -e '"%c"' "$1") = '070701' ]]; then + offset=$(grep -m 1 --byte-offset --only-matching --text $_CPIO_END "$1") + offset=$(( ${offset%%:$_CPIO_END} + ${#_CPIO_END} )) + # skip zero byte at the begining + dd if=$1 bs=$offset skip=1 2>/dev/null | sed '1 s/^\x00*//' +else + exec cat $1 +fi -- 2.0.2
participants (2)
-
Dave Reisner
-
Weng Xuetian