Create a CoreMark Boot Disk for 386

2021-01-08

/uploads/blog/2021/1611013183138-2021-01-18%2018.39.21.jpg

Introduction

Coremark is a cross-platform CPU benchmark tool. I would like to create a Linux boot floppy disk that would boot on a real Intel 80386 machine and then run the coremark. It should support 80386SX with as little as ~8MB of RAM.

WARNING : The method described here is tested and failed to work on real machines / 86box. This project is not considered as done, and may be revisited in the future.

Buildroot

The buildroot is used to compile the gcc 5 that will be used to compile the kernel and the coremark. Linux distribution offered compiler doesn't quite work here because:

  1. We are stuck with Linux 3.2.102 if 386 support is necessary. Linux 3.2 can only be built with gcc 3-5 by default.
  2. The libc bundled with distribution compiler is unlikely to target i386. This cause issues when trying to link them statically into the build.

Buildroot Config

The last buildroot version that supports 386 is the 2016.02 version. Download, extract and run make menuconfig.

Set target architecture to i386, and target machine to 386. Set gcc version to 5.x. In bootloaders, select syslinux, image to install is mbr.

Buildroot Patch

There are several patches that is required to build the system correctly, apply them to the buildroot.

fix problems occuring from changes in glibc 2.28
https://github.com/coreutils/gnulib/commit/4af4a4a71827c0bc5e0ec67af23edef4f15cee8e

diff -ru m4-1.4.17.orig/lib/freadahead.c m4-1.4.17/lib/freadahead.c
--- m4-1.4.17.orig/lib/freadahead.c 2019-06-19 15:21:26.897812071 -0400
+++ m4-1.4.17/lib/freadahead.c  2019-06-19 15:25:40.075547063 -0400
@@ -25,7 +25,7 @@
 size_t
 freadahead (FILE *fp)
 {
-#if defined _IO_ftrylockfile || __GNU_LIBRARY__ == 1 /* GNU libc, BeOS, Haiku, Linux libc5 */
+#if defined _IO_EOF_SEEN || __GNU_LIBRARY__ == 1 /* GNU libc, BeOS, Haiku, Linux libc5 */
   if (fp->_IO_write_ptr > fp->_IO_write_base)
     return 0;
   return (fp->_IO_read_end - fp->_IO_read_ptr)
diff -ru m4-1.4.17.orig/lib/fseeko.c m4-1.4.17/lib/fseeko.c
--- m4-1.4.17.orig/lib/fseeko.c 2019-06-19 15:21:26.897812071 -0400
+++ m4-1.4.17/lib/fseeko.c  2019-06-19 15:27:19.368232257 -0400
@@ -47,7 +47,7 @@
 #endif

   /* These tests are based on fpurge.c.  */
-#if defined _IO_ftrylockfile || __GNU_LIBRARY__ == 1 /* GNU libc, BeOS, Haiku, Linux libc5 */
+#if defined _IO_EOF_SEEN || __GNU_LIBRARY__ == 1 /* GNU libc, BeOS, Haiku, Linux libc5 */
   if (fp->_IO_read_end == fp->_IO_read_ptr
       && fp->_IO_write_ptr == fp->_IO_write_base
       && fp->_IO_save_base == NULL)
@@ -121,7 +121,7 @@
           return -1;
         }

-#if defined _IO_ftrylockfile || __GNU_LIBRARY__ == 1 /* GNU libc, BeOS, Haiku, Linux libc5 */
+#if defined _IO_EOF_SEEN || __GNU_LIBRARY__ == 1 /* GNU libc, BeOS, Haiku, Linux libc5 */
       fp->_flags &= ~_IO_EOF_SEEN;
       fp->_offset = pos;
 #elif defined __sferror || defined __DragonFly__ /* FreeBSD, NetBSD, OpenBSD, DragonFly, Mac OS X, Cygwin */
diff -ru m4-1.4.17.orig/lib/stdio-impl.h m4-1.4.17/lib/stdio-impl.h
--- m4-1.4.17.orig/lib/stdio-impl.h 2019-06-19 15:21:26.909812152 -0400
+++ m4-1.4.17/lib/stdio-impl.h  2019-06-19 15:29:10.789003521 -0400
@@ -21,6 +21,14 @@

 /* BSD stdio derived implementations.  */

+/* Glibc 2.28 made _IO_IN_BACKUP private.  For now, work around this
+   problem by defining it ourselves.  FIXME: Do not rely on glibc
+   internals.  */
+
+#if !defined _IO_IN_BACKUP && defined _IO_EOF_SEEN
+# define _IO_IN_BACKUP 0x100
+#endif
+
 #if defined __NetBSD__                         /* NetBSD */
 /* Get __NetBSD_Version__.  */
 # include <sys/param.h>
disable split-stack for non-thread builds

Signed-off-by: Waldemar Brodkorb <wbx@openadk.org>

diff -Nur gcc-5.3.0.orig/libgcc/config/t-stack gcc-5.3.0/libgcc/config/t-stack
--- gcc-5.3.0.orig/libgcc/config/t-stack    2010-10-01 21:31:49.000000000 +0200
+++ gcc-5.3.0/libgcc/config/t-stack 2016-03-07 03:25:32.000000000 +0100
@@ -1,4 +1,6 @@
 # Makefile fragment to provide generic support for -fsplit-stack.
 # This should be used in config.host for any host which supports
 # -fsplit-stack.
+ifeq ($(enable_threads),yes)
 LIB2ADD_ST += $(srcdir)/generic-morestack.c $(srcdir)/generic-morestack-thread.c
+endif

Linux

Linux dropped the 386 support in 3.8, so the last LTS kernel we could use is the 3.2.102 (which is already EOL).

wget https://cdn.kernel.org/pub/linux/kernel/v3.x/linux-3.2.102.tar.gz
tar xzf linux-3.2.102.tar.gz
cd linux-3.2.102
wget -O .config https://gist.githubusercontent.com/zephray/4766d1161a96fc7924e3115862d14464/raw/8308587e77d4eeaf5cf093bb2766eec44b7d11bf/3.2.102_tinyconfig
make ARCH=x86 CROSS_COMPILE=/home/wenting/Documents/buildroot-2016.02/output/host/usr/bin/i386-linux- -j12

The final built kernel should be 494 KB.

Coremark

Compiling coremark

Clone the coremark from the official git repo:

git clone https://github.com/eembc/coremark

Edit the core_portme.mak for the linux target with the following changes:

--- a/linux/core_portme.mak
+++ b/linux/core_portme.mak
@@ -21,16 +21,16 @@
 OUTFLAG= -o
 # Flag: CC
 #      Use this flag to define compiler to use
-CC = gcc
+CC = /home/wenting/Documents/buildroot-2016.02/output/host/usr/bin/i386-linux-gcc
 # Flag: CFLAGS
 #      Use this flag to define compiler options. Note, you can add compiler options from the command line using XCFLAGS="other flags"
-PORT_CFLAGS = -O2
+PORT_CFLAGS = -O2 -march=i386
 FLAGS_STR = "$(PORT_CFLAGS) $(XCFLAGS) $(XLFLAGS) $(LFLAGS_END)"
 CFLAGS = $(PORT_CFLAGS) -I$(PORT_DIR) -I. -DFLAGS_STR=\"$(FLAGS_STR)\"
 #Flag: LFLAGS_END
 #      Define any libraries needed for linking or other flags that should come at the end of the link line (e.g. linker scripts). 
 #      Note: On certain platforms, the default clock_gettime implementation is supported but requires linking of librt.
-LFLAGS_END += -lrt
+LFLAGS_END += -static-libgcc -static
 # Flag: PORT_SRCS
 # Port specific source files can be added here
 PORT_SRCS = $(PORT_DIR)/core_portme.c

Basically we want it to use our buildroot compiler, target 80386, and link statically.

Though not required, but it is good if we just add an infinite loop at the end of the run so it doesn't exit and shows an error:

--- a/linux/core_portme.c
+++ b/linux/core_portme.c
@@ -258,6 +258,7 @@ void
 portable_fini(core_portable *p)
 {
     p->portable_id = 0;
+    while(1);
 }

 #if (MULTITHREAD > 1)

Pack it into a initrd

We are going to use coremark as the /init so it is being executed right away.

mkdir initrd
cd initrd
cp ../coremark/coremark.exe ./init
find . | sort | cpio -o -H newc -R 0:0 | gzip -9 > ../initrd.cpio.gz

Now we've got a initrd that contains the coremark.

Syslinux

Syslinux is a bootloader that is usually used by Linux installers. It allows booting Linux from CD/ DVD/ PXE/ Floppy/ HDD/ UEFI and all sorts of sources.

However, it is not recommended to use syslinux because the way syslinux detects system memory is generally not implemented on BIOSes before Pentium (P5). This causes the syslinux failes to allocate memory on 386 and 486 machines, even if it runs.

To compile syslinux:

git clone http://repo.or.cz/syslinux.git
cd syslinux
git checkout syslinux-4.07
make

Create floppy disk image

Using Syslinux

dd if=/dev/zero of=coremark.img bs=1k count=1440
mkdosfs coremark.img 
syslinux --install coremark.img 
sudo mount -o loop coremark.img /mnt 
sudo cp linux-3.2.102/arch/x86/boot/bzImage /mnt 
sudo cp initrd.cpio.gz /mnt

Create the SYSLINUX config file /mnt/syslinux.cfg:

DEFAULT linux
LABEL linux
 SAY Booting Linux...
 KERNEL bzImage
 APPEND initrd=initrd.cpio.gz

Unmount the image and we are ready to go.

Using loadlin

Using loadlin (a DOS tool for loading Linux) is a better way to do on 386/486 machines. This time, use a FreeDOS install floppy image. Mount it as loop, delete everything other than the kernel and command.com. Copy in the initrd and linux kernel, as well as the loadlin utility. Write the command in autoexec.bat to let it launch the Linux.