309 lines
15 KiB
Makefile
309 lines
15 KiB
Makefile
offset_names = bytes_os_size \
|
||
bytes_mbr_start \
|
||
bytes_mbr_end \
|
||
bytes_header_32k_start \
|
||
bytes_header_32k_end \
|
||
bytes_iso_start \
|
||
bytes_iso_end \
|
||
bytes_fat12_start \
|
||
bytes_fat12_end \
|
||
bytes_gpt_mirror_start \
|
||
bytes_gpt_mirror_end \
|
||
bytes_zip_start \
|
||
bytes_zip_end
|
||
|
||
more_offset_names = ${offset_names} \
|
||
bytes_fat12_size \
|
||
bytes_gpt_mirror_size \
|
||
bytes_header_32k_size \
|
||
bytes_iso_size \
|
||
bytes_zip_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 \
|
||
${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
|
||
|
||
all: ${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/mkisofs ./utils/mformat ./utils/mcopy: ${bld}/check_makefile # TODO: depend on the mkisofs binary
|
||
cp $$(which $$(basename $@)) $@
|
||
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 ${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,}}
|
||
|
||
# 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_fat12_end} + $${bytes_gpt_mirror_size}, bytes_fat12_end bytes_gpt_mirror_size,}}
|
||
${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,}}
|
||
|
||
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 ${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 ${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}/os.zip: ${bld}/os.32k ${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
|
||
(cd ${bld}/os.32k.tmp/ && zip -X ../os.zip os.32k)
|
||
rm ${bld}/os.32k.tmp/os.32k
|
||
rmdir ${bld}/os.32k.tmp
|
||
|
||
${bld}/os.zip.adjusted: ${bld}/os.zip ${dep_bytes_zip_start} ${bld}/check_makefile
|
||
# TODO: the ZIP file can end with a variable-length comment, this would allow us to hide the GPT mirrors.
|
||
set -x; dd if=/dev/zero bs=1 count=${bytes_zip_start} of=$@
|
||
cat $< >> $@
|
||
zip --adjust-sfx $@
|
||
|
||
gdisk_pipe_commands_slowly=while read str; do echo "$$str"; printf "\033[1;33m%s\033[m\n" "$$str" >&2; sleep 0.01; done
|
||
|
||
commit_hash_as_guid=$$(git log -1 --pretty=format:%H | sed -e 's/^\(.\{8\}\)\(.\{4\}\)\(.\{4\}\)\(.\{4\}\)\(.\{12\}\).*$$/\1-\2-\3-\4-\5/' | tr '[:lower:]' '[:upper:]')
|
||
git_dirty=test -n "$$(git diff --shortstat)"
|
||
gpt_disk_guid=${commit_hash_as_guid}$$(if $$git_dirty; then printf '0'; else printf '2'; fi)
|
||
gpt_partition_guid=${commit_hash_as_guid}$$(if $$git_dirty; then printf '1'; else printf '3'; fi)
|
||
|
||
${os_filename}: ${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=$@
|
||
# pad with zeroes to prepare for GPT table
|
||
set -x; dd if=/dev/zero seek=$$((${bytes_gpt_mirror_end} - 1 )) bs=1 count=1 conv=notrunc 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\n${gpt_disk_guid}\n"; \
|
||
printf "c\n${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
|
||
# 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=$@
|
||
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/^[^ ]\+ \+[^ ]\+ \+//' $< > $@
|