os-test-framework/Makefile.example-os
2018-10-15 00:29:57 +02:00

412 lines
19 KiB
Makefile
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

offset_names = bytes_os_size \
bytes_mbr_start \
bytes_mbr_end \
bytes_gpt_header_start \
bytes_header_32k_start \
bytes_header_32k_end \
bytes_iso_start \
bytes_iso_end \
bytes_fat12_start \
bytes_fat12_end \
bytes_zip_size \
bytes_zip_start \
bytes_zip_end \
zip_byte_before_gpt \
bytes_gpt_mirror_size \
bytes_gpt_mirror_start \
bytes_gpt_header_mirror_start \
bytes_gpt_mirror_end
more_offset_names = ${offset_names} \
bytes_fat12_size \
bytes_header_32k_size \
bytes_iso_size \
sectors_fat12_size \
sectors_fat12_start \
sectors_gpt_mirror_size \
sectors_iso_size \
sectors_os_size \
sectors_zip_size \
tracks_fat12_size \
tracks_gpt_mirror_size \
tracks_iso_size \
tracks_os_size \
tracks_zip_size
more_offset_dec = ${more_offset_names:%=${bld}/offsets/%.dec}
more_offset_hex = ${more_offset_names:%=${bld}/offsets/%.hex}
# + os.arm.disasm
# + os.reasm.disasm
built_files += ${os_filename} \
${bld}/os.ndisasm.disasm \
${bld}/os.reasm.asm \
${bld}/os.reasm \
${bld}/os.file \
${bld}/os.gdisk \
${bld}/os.zip \
${bld}/os.zip.adjusted \
${bld}/os.iso \
${bld}/os.32k \
${bld}/os.fat12 \
${bld}/os.offsets.hex \
${bld}/os.offsets.dec \
${bld}/os.hex_with_offsets \
${bld}/iso_files/os.zip \
${bld}/iso_files/boot/iso_boot.sys \
${bld}/gpt_guid1 \
${bld}/gpt_guid2 \
${bld}/os_with_guid_0 \
${bld}/os_with_guid_hash \
${bld}/os_with_guid_hash.partition_array \
${bld}/os_with_guid_hash.gpt_header_fixed_crc \
${bld}/os_with_guid_hash.fixed_gpt_crc \
${bld}/zip_comment \
${more_offset_dec} \
${more_offset_hex} \
# Temporary copies used to adjust timestamps for reproducible builds.
# These are normally created and deleted within a single target, but
# could remain if make is interrupted during the build.
temp_files = ${bld}/iso_files.tmp/os.zip \
${bld}/iso_files.tmp/boot/iso_boot.sys
temp_directories = ${bld}/iso_files.tmp \
${bld}/iso_files.tmp/boot/
built_directories += ${bld}/iso_files/boot \
${bld}/iso_files \
${bld}/offsets
os_image_size_kb = 1440
os_partition_start_sectors = 3
os_partition_size_sectors = 717 # 720 - start
# CHS parameters for 1.44 MB floppy disk
os_floppy_chs_h = 2
os_floppy_chs_s = 9
in-guix: ${os_filename} \
${bld}/os.ndisasm.disasm \
${bld}/os.reasm.asm \
${bld}/os.file \
${bld}/os.gdisk \
${bld}/os.offsets.hex \
${bld}/os.offsets.dec \
${bld}/os.hex_with_offsets \
${more_offset_dec} \
${more_offset_hex} \
${bld}/check_makefile
# 32k header of the ISO9660 image
${bld}/os.32k: example-os/os.asm ${bld}/check_makefile
nasm -w+macro-params -w+macro-selfref -w+orphan-labels -w+gnu-elf-extensions -o $@ $<
# Circumvent the fact that faketime does not work on system binaries in macos
./utils/mformat ./utils/mcopy: ${bld}/check_makefile # TODO: depend on the relevant binary
cp $$(which $$(basename $@)) $@
chmod u+x $@
./utils/mkisofs: ${bld}/check_makefile # TODO: depend on the mkisofs binary
cp $$(which $$(basename $@) || which xorriso) $@
chmod u+x $@
cp_T_option = $$(if test "$$(uname -s)" = Darwin; then echo ''; else echo '-T'; fi)
${bld}/os.iso: ${bld}/iso_files/os.zip ${bld}/iso_files/boot/iso_boot.sys ./utils/mkisofs ${bld}/check_makefile
! test -d ${bld}/iso_files.tmp
cp -a ${cp_T_option} -- ${bld}/iso_files ${bld}/iso_files.tmp
find ${bld}/iso_files.tmp -depth -exec touch -t ${commit_timestamp} '{}' ';'
UTILS="$$PWD/utils" ./utils/faketime.sh ${real_commit_timestamp_iso_8601} \
sh -c '(cd ./${bld}/iso_files.tmp/ && "$$UTILS/mkisofs" \
--input-charset utf-8 \
-rock \
-joliet \
-eltorito-catalog boot/boot.cat \
-eltorito-boot boot/iso_boot.sys \
-no-emul-boot \
-boot-load-size 4 \
-pad \
-output ../os.iso \
.)'
rm -- ${bld}/iso_files.tmp/os.zip \
${bld}/iso_files.tmp/boot/iso_boot.sys
rmdir ${bld}/iso_files.tmp/boot/
rmdir ${bld}/iso_files.tmp/
# Layout:
# MBR; GPT; UNIX sh & MS-DOS batch scripts; ISO9660; FAT12; GPT mirror; ZIP
define offset
tmp_${1} = ${3}
${bld}/offsets/${1}.dec: $${tmp_${1}:%=${bld}/offsets/%.dec} ${4} ${bld}/check_makefile
echo $$$$(( ${2} )) | tee $$@
${1} = $$$$(cat ${bld}/offsets/${1}.dec)
dep_${1} = ${bld}/offsets/${1}.dec
endef
define div_round_up
( ( ( ${1} ) + ( ${2} ) - 1 ) / ( ${2} ) )
endef
sector_size = 512
# should be exact (TODO: make a check)
${eval ${call offset,bytes_os_size, $${os_image_size_kb} * 1024,, }}
${eval ${call offset,sectors_os_size, $${bytes_os_size} / $${sector_size}, bytes_os_size,}}
${eval ${call offset,tracks_os_size, $${sectors_os_size} / $${os_floppy_chs_s}, sectors_os_size,}}
# Primary GPT
${eval ${call offset,bytes_gpt_header_start, $${sector_size},, }}
# round up
${eval ${call offset,bytes_iso_size, $$$$(utils/file-length.sh -c ${bld}/os.iso), ,${bld}/os.iso}}
${eval ${call offset,sectors_iso_size, ${call div_round_up,$${bytes_iso_size},$${sector_size}}, bytes_iso_size,}}
${eval ${call offset,tracks_iso_size, ${call div_round_up,$${sectors_iso_size},$${os_floppy_chs_s}}, sectors_iso_size,}}
# round up
${eval ${call offset,bytes_zip_size, $$$$(utils/file-length.sh -c ${bld}/os.zip), ,${bld}/os.zip}}
${eval ${call offset,sectors_zip_size, ${call div_round_up,$${bytes_zip_size},$${sector_size}}, bytes_zip_size,}}
${eval ${call offset,tracks_zip_size, ${call div_round_up,$${sectors_zip_size},$${os_floppy_chs_s}}, sectors_zip_size,}}
# round up
${eval ${call offset,sectors_gpt_mirror_size, 33,, }}
${eval ${call offset,tracks_gpt_mirror_size, ${call div_round_up,$${sectors_gpt_mirror_size},$${os_floppy_chs_s}}, sectors_gpt_mirror_size,}}
# allocate the remaining sectors to the FAT, aligned on tracks
${eval ${call offset,tracks_fat12_size, $${tracks_os_size} - $${tracks_iso_size} - $${tracks_gpt_mirror_size} - $${tracks_zip_size}, tracks_os_size tracks_iso_size tracks_gpt_mirror_size tracks_zip_size,}}
${eval ${call offset,sectors_fat12_size,$${tracks_fat12_size} * $${os_floppy_chs_s}, tracks_fat12_size,}}
# zip should probably have its end aligned, not its start
${eval ${call offset,bytes_zip_start, $${bytes_os_size} - $${bytes_zip_size}, bytes_os_size bytes_zip_size,}}
${eval ${call offset,bytes_mbr_start, 0,,}}
${eval ${call offset,bytes_mbr_end, 512,,}}
${eval ${call offset,bytes_header_32k_start, 0,,}}
${eval ${call offset,bytes_header_32k_end, 32 * 1024,,}}
${eval ${call offset,bytes_header_32k_size, $${bytes_header_32k_end} - $${bytes_header_32k_start}, bytes_header_32k_end bytes_header_32k_start,}}
${eval ${call offset,bytes_iso_start, 32 * 1024,,}}
${eval ${call offset,bytes_iso_end, $${sectors_iso_size} * $${sector_size}, sectors_iso_size,}}
${eval ${call offset,bytes_fat12_start, $${tracks_iso_size} * $${os_floppy_chs_s} * $${sector_size}, tracks_iso_size,}}
${eval ${call offset,sectors_fat12_start, $${bytes_fat12_start} / $${sector_size}, bytes_fat12_start,}}
${eval ${call offset,bytes_fat12_size, $${sectors_fat12_size} * $${sector_size}, sectors_fat12_size,}}
${eval ${call offset,bytes_fat12_end, $${bytes_fat12_start} + $${bytes_fat12_size}, bytes_fat12_start bytes_fat12_size,}}
# It is probably not necessary to align the GPT mirror end on a track boundary.
${eval ${call offset,bytes_gpt_mirror_size, $${sectors_gpt_mirror_size} * $${sector_size}, sectors_gpt_mirror_size,}}
${eval ${call offset,bytes_gpt_mirror_end, $${bytes_os_size}, bytes_os_size,}}
${eval ${call offset,bytes_gpt_header_mirror_start, $${bytes_gpt_mirror_end} - $${sector_size}, bytes_gpt_mirror_end,}}
${eval ${call offset,bytes_gpt_mirror_start, $${bytes_gpt_mirror_end} - $${bytes_gpt_mirror_size}, bytes_gpt_mirror_end bytes_gpt_mirror_size,}}
${eval ${call offset,bytes_zip_end, $${bytes_os_size}, bytes_os_size,}}
${eval ${call offset,zip_byte_before_gpt, $${bytes_gpt_mirror_start} - 1, bytes_gpt_mirror_start,}}
os_fat12_partition = "$@@@${bytes_fat12_start}"
${bld}/os.fat12: ${bld}/os.zip ${dep_bytes_fat12_size} ${dep_bytes_fat12_start} ${dep_sectors_os_size} \
./utils/mformat ./utils/mcopy ${bld}/check_makefile
set -x; dd if=/dev/zero bs=${sector_size} count=${sectors_os_size} of=$@
./utils/faketime.sh ${real_commit_timestamp_iso_8601} ./utils/mformat -v "Example OS" \
-T ${sectors_fat12_size} \
-h ${os_floppy_chs_h} \
-s ${os_floppy_chs_s} \
-i ${os_fat12_partition}
./utils/faketime.sh ${real_commit_timestamp_iso_8601} ./utils/mcopy -i ${os_fat12_partition} $< "::os.zip"
${bld}/iso_files/os.zip: ${bld}/os.zip ${bld}/check_makefile
# TODO: make it so that the various file formats are mutual quines:
# * the ISO should contain the original file
# * the ZIP should contain the original file
# * the FAT12 should contain the original file
cp $< $@
# 4 sectors loaded when booting from optical media (CD-ROM, …):
${bld}/iso_files/boot/iso_boot.sys: ${bld}/os.32k ${bld}/check_makefile
# TODO: this copy of the (or alternate) bootsector should contain a Boot Information Table,
# see https://wiki.osdev.org/El-Torito#A_BareBones_Boot_Image_with_Boot_Information_Table
dd if=$< bs=512 count=4 of=$@
${bld}/zip_comment: ${dep_bytes_gpt_mirror_size} ${bld}/check_makefile
# Reserve enough space for the secondary GPT.
#
# The X will be replaced by a 0x00 byte after the zip is created and
# adjusted, because the zip utility otherwise truncates the archive
# comment.
#
# It seems that the zip utility injects extra '\r\n' line endings after
# every 256 characters, which changes the total length of the
# comment. We therefore insert a \n directly (zip seems to convert line
# endings from Unix \n to DOS \r\n), and aim for the exact desired size.
(printf "%sX\n" '${os_name}'; \
for i in $$(seq $$((${sectors_gpt_mirror_size} * 2))); do \
printf "%254s\n" ''; \
done) > $@
${bld}/os.zip: ${bld}/os.32k ${bld}/zip_comment ${bld}/check_makefile
# We copy os.32k and alter its timestamp to ensure reproducible
# builds.
mkdir -p ${bld}/os.32k.tmp
cp -a $< ${bld}/os.32k.tmp/os.32k
touch -t ${commit_timestamp} ${bld}/os.32k.tmp/os.32k
# If there is already an archive present from a previous build, zip
# risks trying to update it, so we have to delete it if it exists.
rm -f $@
(cd ${bld}/os.32k.tmp/ && zip -X --archive-comment ../os.zip os.32k < ../zip_comment)
rm ${bld}/os.32k.tmp/os.32k
rmdir ${bld}/os.32k.tmp
${bld}/os.zip.adjusted: ${bld}/os.zip ${dep_bytes_zip_start} ${dep_zip_byte_before_gpt} ${bld}/check_makefile
set -x; dd if=/dev/zero bs=1 count=${bytes_zip_start} of=$@
cat $< >> $@
zip --adjust-sfx $@
# Mark the end of the real ZIP comment, so that the rest is not printed.
# The rest of the comment contains the GPT mirror.
set -x; dd if=/dev/zero bs=1 count=1 conv=notrunc seek=${zip_byte_before_gpt} of=$@
gdisk_pipe_commands_slowly=while read str; do echo "$$str"; printf "\033[1;33m%s\033[m\n" "$$str" >&2; sleep 0.01; done
# The last digit of guid1 is 1, for guid2 it's 2, and so on.
# This allows us to have a disk GUID which is different from the partition GUID
${bld}/gpt_guid%: ${bld}/os_with_guid_0 ${bld}/check_makefile
sha256sum $< \
| sed -e 's/^\(.\{8\}\)\(.\{4\}\)\(.\{4\}\)\(.\{4\}\)\(.\{11\}\).*$$/\1-\2-\3-\4-\5$*/' \
| tr '[:lower:]' '[:upper:]' \
> $@
gpt_disk_guid=${bld}/gpt_guid1
gpt_partition_guid=${bld}/gpt_guid2
${bld}/os_with_guid_0: ${bld}/os.32k ${bld}/os.iso ${bld}/os.fat12 ${bld}/os.zip.adjusted \
${dep_bytes_header_32k_start} \
${dep_bytes_header_32k_size} \
${dep_bytes_fat12_start} \
${dep_bytes_fat12_size} \
${dep_bytes_gpt_mirror_start} \
${dep_bytes_gpt_mirror_end} \
${dep_sectors_fat12_start} \
${dep_sectors_fat12_size} \
${dep_bytes_zip_start} \
${bld}/check_makefile
rm -f $@
# start with the .iso
cp ${bld}/os.iso $@
# splice in the first 32k (bootsector and partition table)
set -x; dd skip=${bytes_header_32k_start} seek=${bytes_header_32k_start} bs=1 count=${bytes_header_32k_size} conv=notrunc if=${bld}/os.32k of=$@
# splice in fat12
set -x; dd skip=${bytes_fat12_start} seek=${bytes_fat12_start} bs=1 count=${bytes_fat12_size} conv=notrunc if=${bld}/os.fat12 of=$@
# splice in zip at the end
set -x; dd skip=${bytes_zip_start} seek=${bytes_zip_start} bs=1 conv=notrunc if=${bld}/os.zip.adjusted of=$@
# patch the partition table
# Thanks to https://wiki.gentoo.org/wiki/Hybrid_partition_table for showing that gdisk can be used to make a hybrid MBR / GPT.
# gdisk commands:
# * Delete (the only) partition, eXpert mode, sector aLignment = 1, back to Main menu,
# * New partition (number = 1, start sector, end sector, type 0700)
# * Recovery and transformation options, make Hybrid,
# * add GPT partition #1 to the hybrid MBR, do Not put the EFI partition first,
# * MBR partition type=0x01, bootable=Yes, do Not add extra partitions,
# * back to Main menu,
# * eXpert mode,
# * Change partition GUID, the GUID itself,
# * change disk GUID, the GUID itself,
# * Print GPT, print prOtective MBR, Write, Proceed.
(if test "$$(uname -o)" = "Cygwin"; then printf "Y\n"; fi; \
printf "d\nx\nl\n1\nm\n"; \
printf "n\n1\n${sectors_fat12_start}\n${sectors_fat12_size}\n0700\n"; \
printf "r\nh\n"; \
printf "1\nN\n"; \
printf "01\nY\nN\n"; \
printf "x\n"; \
printf "g\n00000000-0000-0000-0000-000000000000\n"; \
printf "c\n00000000-0000-0000-0000-000000000000\n"; \
printf "p\no\nw\nY\n") | ${gdisk_pipe_commands_slowly} | gdisk $@
${bld}/os_with_guid_hash: ${bld}/os_with_guid_0 \
${gpt_disk_guid} ${gpt_partition_guid} \
${bld}/check_makefile
cp $< $@
# gdisk commands:
# * eXpert mode,
# * Change partition GUID, the GUID itself,
# * change disk GUID, the GUID itself,
# * Print GPT, print prOtective MBR, Write, Proceed.
(if test "$$(uname -o)" = "Cygwin"; then printf "Y\n"; fi; \
printf "x\n"; \
printf "g\n$$(cat ${gpt_disk_guid})\n"; \
printf "c\n$$(cat ${gpt_partition_guid})\n"; \
printf "p\no\nw\nY\n") | ${gdisk_pipe_commands_slowly} | gdisk $@
# Inject MS-DOS newlines (CR+LF) and comments (":: ") in the GUID field of unused partition table entries,
# so that the part that is to be skipped by MS-DOS does not form a line longer than the MS-DOS maximum
# line length (8192 excluding CR+LF). $i below is the partition entry number, starting from 1
# The numbers 55 and 118 are arbitrarily chosen so that the space between two CR+LF is less than 8192.
for i in 55 118; do \
printf "\r\n:: %02x" $$i | dd bs=1 seek=$$(( 1024 + ( ($$i) - 1) * 128 + 16)) count=7 conv=notrunc of=$@; \
printf "\r\n:: %02x" $$i | dd bs=1 seek=$$(( ${bytes_gpt_mirror_start} + ( ($$i) - 1) * 128 + 16)) count=7 conv=notrunc of=$@; \
done
%.partition_array: % ${bld}/check_makefile
# Extract the partition array header
dd if=$< bs=512 skip=2 count=32 of=$@
# The GPT header of the given disk image, with its CRC adjusted to the actual
# data. It is used to fix the CRC after injecting MS-DOS comments in ignored
# parts of the partition array.
%.gpt_header_fixed_crc: % %.partition_array % ${bld}/check_makefile
# Extract the GPT header
dd if=$< bs=512 skip=1 count=1 of=$@
# * Compute the CRC32 of the particion array
# * Convert endianness
# * Convert to binary
# * Inject into the GPT header
crc32 $*.partition_array \
| sed -e 's/^\(..\)\(..\)\(..\)\(..\)$$/\4\3\2\1/' \
| xxd -r -ps \
| dd bs=1 seek=88 count=4 conv=notrunc of=$@
# Zero-out the CRC of the header itself
printf '00000000' | xxd -r -ps | dd bs=1 seek=16 count=4 conv=notrunc of=$@
# * Compute the CRC32 of the GPT header (with the CRC field itself zeroed out)
# * Convert endianness
# * Convert to binary
# * Inject into the GPT header
crc32 $@ \
| sed -e 's/^\(..\)\(..\)\(..\)\(..\)$$/\4\3\2\1/' \
| xxd -r -ps \
| dd bs=1 seek=16 count=4 conv=notrunc of=$@
%.fixed_gpt_crc: % %.gpt_header_fixed_crc \
${dep_bytes_gpt_header_start} ${dep_bytes_gpt_header_mirror_start} \
${bld}/check_makefile
cp $< $@
# Inject the fixed GPT header
dd if=$*.gpt_header_fixed_crc bs=1 seek=${bytes_gpt_header_start} count=${sector_size} conv=notrunc of=$@
# Inject the fixed GPT header in the mirror
dd if=$*.gpt_header_fixed_crc bs=1 seek=${bytes_gpt_header_mirror_start} count=${sector_size} conv=notrunc of=$@
${os_filename}: ${bld}/os_with_guid_hash.fixed_gpt_crc ${bld}/check_makefile
cp -f $< $@
chmod a+x-w $@
${bld}/os.file: ${os_filename} ${bld}/check_makefile
file -kr $< > $@
${bld}/os.gdisk: ${os_filename} ${bld}/check_makefile
# gdisk commands:
# * eXpert mode
# * Print partition table
#  * print detailed Information about the (only) partition
# * print prOtective MBR table
# * Quit
printf '2\nx\np\ni\nr\no\nq\n' | ${gdisk_pipe_commands_slowly} | gdisk $< | tee $@
${bld}/os.offsets.hex: ${offset_names:%=${bld}/offsets/%.hex} ${bld}/check_makefile
grep '^' ${offset_names:%=${bld}/offsets/%.hex} | sed -e 's|^.*/||' -e 's/:/: 0x/' | column -t > $@
${bld}/os.offsets.dec: ${offset_names:%=${bld}/offsets/%.dec} ${bld}/check_makefile
grep '^' ${offset_names:%=${bld}/offsets/%.dec} | sed -e 's|^.*/||' -e 's/:/: /' | column -t > $@
${bld}/offsets/%.hex: ${bld}/offsets/%.dec
printf '%x\n' $$(cat $<) > $@
${bld}/os.hex_with_offsets: ${os_filename} ${bld}/os.offsets.hex
hexdump -C $< \
| grep -E -e "($$(cat ${bld}/os.offsets.hex | cut -d '=' -f 2 | sed -e 's/^[[:space:]]*0x\(.*\).$$/^\10/' | tr '\n' '|')^)" --color=yes > $@
${bld}/os.ndisasm.disasm: ${os_filename} utils/compact-ndisasm.sh ${bld}/check_makefile
./utils/compact-ndisasm.sh $< $@
${bld}/os.reasm.asm: ${bld}/os.ndisasm.disasm ${bld}/check_makefile
sed -e 's/^[^ ]\+ \+[^ ]\+ \+//' $< > $@