On Tue, Jan 28, 2014 at 8:30 AM, Uros Bizjak <ubiz...@gmail.com> wrote:
> On Tue, Jan 28, 2014 at 5:01 PM, Uros Bizjak <ubiz...@gmail.com> wrote:
>> On Mon, Jan 27, 2014 at 8:44 PM, H.J. Lu <hongjiu...@intel.com> wrote:
>>
>>> The .code16gcc directive was added to binutils back in 1999:
>>>
>>> ---
>>>    '.code16gcc' provides experimental support for generating 16-bit code
>>> from gcc, and differs from '.code16' in that 'call', 'ret', 'enter',
>>> 'leave', 'push', 'pop', 'pusha', 'popa', 'pushf', and 'popf'
>>> instructions default to 32-bit size.  This is so that the stack pointer
>>> is manipulated in the same way over function calls, allowing access to
>>> function parameters at the same stack offsets as in 32-bit mode.
>>> '.code16gcc' also automatically adds address size prefixes where
>>> necessary to use the 32-bit addressing modes that gcc generates.
>>> ---
>>>
>>> It encodes 32-bit assembly instructions generated by GCC in 16-bit format
>>>  so that GCC can be used to generate 16-bit instructions.  To do that, the
>>>  .code16gcc directive may be placed at the very beginning of the assembly
>>>  code.  This patch adds -m16 to x86 backend by:
>>>
>>> 1. Add -m16 and make it mutually exclusive with -m32, -m64 and -mx32.
>>> 2. Treat -m16 like -m32 so that --32 is passed to assembler.
>>> 3. Output .code16gcc at the very beginning of the assembly code.
>>> 4. Turn off 64-bit ISA when -m16 is used.
>>>
>>> Tested on Linux/x86 and Linux/x86-64.  OK for trunk?
>>>
>>> Thanks.
>>>
>>> H.J.
>>> ---
>>>         PR target/59672
>>>         * config/i386/gnu-user64.h (SPEC_32): Add "m16|" to "m32".
>>>         (SPEC_X32): Likewise.
>>>         (SPEC_64): Likewise.
>>>         * config/i386/i386.c (ix86_option_override_internal): Turn off
>>>         OPTION_MASK_ISA_64BIT, OPTION_MASK_ABI_X32 and OPTION_MASK_ABI_64
>>>         for TARGET_16BIT.
>>>         (x86_file_start): Output .code16gcc for TARGET_16BIT.
>>>         * config/i386/i386.h (TARGET_16BIT): New macro.
>>>         (TARGET_16BIT_P): Likewise.
>>>         * config/i386/i386.opt: Add m16.
>>>         * doc/invoke.texi: Document -m16.
>>
>> OK for mainline, needs OK from RMs for a backport.
>>
>> Please also add the entry to Changes.html, this is user-visible change.
>
> Oh, a short scan-asm testcase would be nice, too.
>

scan-asm testcase doesn't do anything useful.  The only
difference in assembly code between -m16 and -m32 is the
.code16gcc directive  All magic is done in assembler.

Here is a run-time test.  It builds 16-bit image for BIOS and
loads it into qemu-system-i386.  OK to install?

Thanks.

-- 
H.J.
---
PR target/59672
* gcc.target/i386/m16/Makefile: New file.
* gcc.target/i386/m16/m16.exp: Likewise.
* gcc.target/i386/m16/include16/string.h: Likewise.
* gcc.target/i386/m16/include16/sys16.h: Likewise.
* gcc.target/i386/m16/lib16/conio.c: Likewise.
* gcc.target/i386/m16/lib16/crt0.c: Likewise.
* gcc.target/i386/m16/lib16/exit.c: Likewise.
* gcc.target/i386/m16/lib16/strlen.c: Likewise.
* gcc.target/i386/m16/run/run16.c: Likewise.
* gcc.target/i386/m16/run/wrap.S: Likewise.
* gcc.target/i386/m16/run/wwrap.S: Likewise.
* gcc.target/i386/m16/test16/test1.c: Likewise.
From 67f20cece3a500c26ace8992d868bbaeec53e4a4 Mon Sep 17 00:00:00 2001
From: "H.J. Lu" <hjl.to...@gmail.com>
Date: Thu, 23 Jan 2014 06:51:14 -0800
Subject: [PATCH 2/2] Initial -m16 test

	PR target/59672
	* gcc.target/i386/m16/Makefile: New file.
	* gcc.target/i386/m16/m16.exp: Likewise.
	* gcc.target/i386/m16/include16/string.h: Likewise.
	* gcc.target/i386/m16/include16/sys16.h: Likewise.
	* gcc.target/i386/m16/lib16/conio.c: Likewise.
	* gcc.target/i386/m16/lib16/crt0.c: Likewise.
	* gcc.target/i386/m16/lib16/exit.c: Likewise.
	* gcc.target/i386/m16/lib16/strlen.c: Likewise.
	* gcc.target/i386/m16/run/run16.c: Likewise.
	* gcc.target/i386/m16/run/wrap.S: Likewise.
	* gcc.target/i386/m16/run/wwrap.S: Likewise.
	* gcc.target/i386/m16/test16/test1.c: Likewise.
---
 gcc/ChangeLog.m16                                  |  19 ++
 gcc/testsuite/gcc.target/i386/m16/Makefile         |  89 ++++++
 .../gcc.target/i386/m16/include16/string.h         |   6 +
 .../gcc.target/i386/m16/include16/sys16.h          |  24 ++
 gcc/testsuite/gcc.target/i386/m16/lib16/conio.c    |  19 ++
 gcc/testsuite/gcc.target/i386/m16/lib16/crt0.c     |   9 +
 gcc/testsuite/gcc.target/i386/m16/lib16/exit.c     |   9 +
 gcc/testsuite/gcc.target/i386/m16/lib16/strlen.c   |  11 +
 gcc/testsuite/gcc.target/i386/m16/m16.exp          |  55 ++++
 gcc/testsuite/gcc.target/i386/m16/run/run16.c      | 312 +++++++++++++++++++++
 gcc/testsuite/gcc.target/i386/m16/run/wrap.S       | 180 ++++++++++++
 gcc/testsuite/gcc.target/i386/m16/run/wwrap.S      |  13 +
 gcc/testsuite/gcc.target/i386/m16/test16/test1.c   |   7 +
 13 files changed, 753 insertions(+)
 create mode 100644 gcc/testsuite/gcc.target/i386/m16/Makefile
 create mode 100644 gcc/testsuite/gcc.target/i386/m16/include16/string.h
 create mode 100644 gcc/testsuite/gcc.target/i386/m16/include16/sys16.h
 create mode 100644 gcc/testsuite/gcc.target/i386/m16/lib16/conio.c
 create mode 100644 gcc/testsuite/gcc.target/i386/m16/lib16/crt0.c
 create mode 100644 gcc/testsuite/gcc.target/i386/m16/lib16/exit.c
 create mode 100644 gcc/testsuite/gcc.target/i386/m16/lib16/strlen.c
 create mode 100644 gcc/testsuite/gcc.target/i386/m16/m16.exp
 create mode 100644 gcc/testsuite/gcc.target/i386/m16/run/run16.c
 create mode 100644 gcc/testsuite/gcc.target/i386/m16/run/wrap.S
 create mode 100644 gcc/testsuite/gcc.target/i386/m16/run/wwrap.S
 create mode 100644 gcc/testsuite/gcc.target/i386/m16/test16/test1.c

diff --git a/gcc/ChangeLog.m16 b/gcc/ChangeLog.m16
index 05424d9..feb1891 100644
--- a/gcc/ChangeLog.m16
+++ b/gcc/ChangeLog.m16
@@ -1,3 +1,22 @@
+gcc/testsuite/
+
+2014-01-23  H. Peter Anvin  <h...@zytor.com>
+	    H.J. Lu  <hongjiu...@intel.com>
+
+	PR target/59672
+	* gcc.target/i386/m16/Makefile: New file.
+	* gcc.target/i386/m16/m16.exp: Likewise.
+	* gcc.target/i386/m16/include16/string.h: Likewise.
+	* gcc.target/i386/m16/include16/sys16.h: Likewise.
+	* gcc.target/i386/m16/lib16/conio.c: Likewise.
+	* gcc.target/i386/m16/lib16/crt0.c: Likewise.
+	* gcc.target/i386/m16/lib16/exit.c: Likewise.
+	* gcc.target/i386/m16/lib16/strlen.c: Likewise.
+	* gcc.target/i386/m16/run/run16.c: Likewise.
+	* gcc.target/i386/m16/run/wrap.S: Likewise.
+	* gcc.target/i386/m16/run/wwrap.S: Likewise.
+	* gcc.target/i386/m16/test16/test1.c: Likewise.
+
 gcc/
 
 2014-01-23  H.J. Lu  <hongjiu...@intel.com>
diff --git a/gcc/testsuite/gcc.target/i386/m16/Makefile b/gcc/testsuite/gcc.target/i386/m16/Makefile
new file mode 100644
index 0000000..c805701
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/m16/Makefile
@@ -0,0 +1,89 @@
+CC	  = gcc
+LD	  = ld
+AR	  = ar
+OBJCOPY   = objcopy
+
+GCCWARN   = -Wall -Wstrict-prototypes
+CFLAGS	  = -g -m32 -O2 -fno-strict-aliasing $(GCCWARN)
+SFLAGS    = $(CFLAGS) -D__ASSEMBLY__
+LDFLAGS   = $(CFLAGS)
+M16       = -m16
+
+SRC-DIR   = .
+VPATH	  = $(SRC-DIR)
+X16FLAGS  = -g $(M16) -D__SYS16__ -I $(SRC-DIR)/include16
+S16FLAGS  = $(X16FLAGS) -D__ASSEMBLY__
+C16OPTFLAGS = -Os
+C16FLAGS  = $(X16FLAGS) $(GCCWARN) -march=i386 -mregparm=3 \
+	     $(C16OPTFLAGS) -ffreestanding \
+	    -fno-stack-protector -mpreferred-stack-boundary=2 \
+	    -fno-pic
+
+LD16FLAGS    = -m elf_i386 --section-start=.text=0x1000
+LDWRAPFLAGS  = -m elf_i386 --section-start=.text=0x8000
+
+LIB16S    = 
+LIB16C    = conio.c crt0.c exit.c strlen.c
+LIB16C   := $(addprefix lib16/,$(LIB16C))
+LIB16O    = $(LIB16C:.c=.o) $(LIB16S:.S=.o)
+
+TEST1O    = test16/test1.o
+TEST1ELF  = $(TEST1O:.o=.elf)
+
+all : run16 $(TEST1ELF)
+	./run16 $(TEST1ELF)
+
+lib16/%.o: lib16/%.S
+	mkdir -p lib16
+	$(CC) $(S16FLAGS) -c -o $@ $<
+
+lib16/%.o: lib16/%.c
+	mkdir -p lib16
+	$(CC) $(C16FLAGS) -c -o $@ $<
+
+lib16/lib16.a: $(LIB16O)
+	mkdir -p $(@D)
+	rm -f $@
+	$(AR) cq $@ $^
+
+test16/%.o: test16/%.S
+	mkdir -p $(@D)
+	$(CC) $(S16FLAGS) -c -o $@ $<
+
+test16/%.o: test16/%.c
+	mkdir -p $(@D)
+	$(CC) $(C16FLAGS) -c -o $@ $<
+
+test16/%.elf: test16/%.o lib16/lib16.a
+	mkdir -p $(@D)
+	$(LD) $(LD16FLAGS) -o $@ $^
+
+run/wrap.elf: run/wrap.o
+	mkdir -p $(@D)
+	$(LD) $(LDWRAPFLAGS) -o $@ $^
+
+run/wrap.bin: run/wrap.elf
+	mkdir -p $(@D)
+	$(OBJCOPY) -O binary $< $@
+
+run/wwrap.o: run/wwrap.S run/wrap.bin
+
+run/%.o: run/%.S
+	mkdir -p $(@D)
+	$(CC) $(SFLAGS) -c -o $@ $<
+
+run/%.o: run/%.c
+	mkdir -p $(@D)
+	$(CC) $(CFLAGS) -c -o $@ $<
+
+run16: run/run16.o run/wwrap.o
+	mkdir -p $(@D)
+	$(CC) $(LDFLAGS) -o $@ $^
+
+clean:
+	rm -f run16 *.o lib16/*.o lib16/*.a
+	rm -f test16/*.o test16/*.elf test16/*.bin
+	rm -f run/*.o run/*.elf run/*.bin
+
+spotless: clean
+	rm -f *~ */*~
diff --git a/gcc/testsuite/gcc.target/i386/m16/include16/string.h b/gcc/testsuite/gcc.target/i386/m16/include16/string.h
new file mode 100644
index 0000000..ec50591
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/m16/include16/string.h
@@ -0,0 +1,6 @@
+#ifndef _STRING_H
+#define _STRING_H
+
+int strlen(const char *);
+
+#endif /* _STRING_H */
diff --git a/gcc/testsuite/gcc.target/i386/m16/include16/sys16.h b/gcc/testsuite/gcc.target/i386/m16/include16/sys16.h
new file mode 100644
index 0000000..1073009
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/m16/include16/sys16.h
@@ -0,0 +1,24 @@
+#ifndef SYS16_H
+#define SYS16_H
+
+#ifdef __SYS16__
+# define _PTR16(x) x
+#else
+# define _PTR16(x) unsigned int
+#endif
+
+struct system_struct {
+	_PTR16(void *) entrypoint;
+	int argc;
+	_PTR16(char **)argv;
+};
+
+#undef _PTR16
+
+#define SYS_STRUCT_ADDR 0xf000
+
+#ifdef __SYS16__
+# define SYS ((struct system_struct *)SYS_STRUCT_ADDR)
+#endif
+
+#endif /* SYS16_H */
diff --git a/gcc/testsuite/gcc.target/i386/m16/lib16/conio.c b/gcc/testsuite/gcc.target/i386/m16/lib16/conio.c
new file mode 100644
index 0000000..5d5871a
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/m16/lib16/conio.c
@@ -0,0 +1,19 @@
+#include <string.h>
+#include <sys16.h>
+
+int write(int fd, const void *buf, unsigned int count)
+{
+	const char *p = buf;
+	unsigned int c = count;
+
+	while (c--)
+		asm volatile("outb %0,$0xe9" : : "a" (*p++));
+
+	return count;
+}
+
+void puts(const char *s)
+{
+	/* XXX: should loop over this */
+	write(1, s, strlen(s));
+}
diff --git a/gcc/testsuite/gcc.target/i386/m16/lib16/crt0.c b/gcc/testsuite/gcc.target/i386/m16/lib16/crt0.c
new file mode 100644
index 0000000..34f6b36
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/m16/lib16/crt0.c
@@ -0,0 +1,9 @@
+#include <sys16.h>
+
+extern int main(int, char **);
+extern void exit(int);
+
+void _start(void)
+{
+	exit(main(SYS->argc, SYS->argv));
+}
diff --git a/gcc/testsuite/gcc.target/i386/m16/lib16/exit.c b/gcc/testsuite/gcc.target/i386/m16/lib16/exit.c
new file mode 100644
index 0000000..991bd23
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/m16/lib16/exit.c
@@ -0,0 +1,9 @@
+void exit(int c)
+{
+	unsigned char x = c ? 'F' : 'S';
+	unsigned short port = 0x501;
+
+	asm volatile("outb %0,%1" : : "a" (x), "d" (port));
+	for(;;)
+		asm volatile("hlt");
+}
diff --git a/gcc/testsuite/gcc.target/i386/m16/lib16/strlen.c b/gcc/testsuite/gcc.target/i386/m16/lib16/strlen.c
new file mode 100644
index 0000000..7d6dd15
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/m16/lib16/strlen.c
@@ -0,0 +1,11 @@
+#include <string.h>
+
+int strlen(const char *c)
+{
+	const char *c0 = c;
+
+	while (*c)
+		c++;
+
+	return c - c0;
+}
diff --git a/gcc/testsuite/gcc.target/i386/m16/m16.exp b/gcc/testsuite/gcc.target/i386/m16/m16.exp
new file mode 100644
index 0000000..a67c827
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/m16/m16.exp
@@ -0,0 +1,55 @@
+# Expect script for -m16 tests
+# Copyright (C) 2014 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 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 General Public License for more details.
+# 
+# You should have received a copy of the GNU General Public License
+# along with GCC; see the file COPYING3.  If not see
+# <http://www.gnu.org/licenses/>.
+
+# Load support procs.
+load_lib c-torture.exp
+load_lib target-supports.exp
+load_lib torture-options.exp
+
+# Exit immediately if this isn't a native Linux/x86 target.
+if { ![isnative]
+     || ![check_effective_target_ia32]
+     || ![istarget *-*-linux*]
+     || [istarget *-*-linux*aout*]
+     || [istarget *-*-linux*oldld*] } {
+    return
+}
+
+# Check if qemu-system-i386 exists.
+set status [catch "exec qemu-system-i386 -h" exec_output]
+if { $status != 0 } {
+    return
+}
+
+catch "exec mkdir -p m16" status
+set status [catch "exec cp -L $srcdir/$subdir/Makefile m16" exec_output]
+if { $status != 0 } {
+    unresolved "-m16"
+    send_log "$exec_output\n"
+    return
+}
+
+foreach opt $C_TORTURE_OPTIONS {
+    set status [catch "exec make -C m16 clean" exec_output]
+    set status [catch "exec make -C m16 SRC-DIR=$srcdir/$subdir \"CC=$GCC_UNDER_TEST\" \"C16OPTFLAGS=$opt\"" exec_output]
+    send_log "$exec_output\n"
+    if { $status != 0 } {
+	fail "-m16 $opt"
+    } else {
+	pass "-m16 $opt"
+    }
+}
diff --git a/gcc/testsuite/gcc.target/i386/m16/run/run16.c b/gcc/testsuite/gcc.target/i386/m16/run/run16.c
new file mode 100644
index 0000000..a81c84a
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/m16/run/run16.c
@@ -0,0 +1,312 @@
+/*
+ * Wrapper to run a 16-bit raw binary as a "BIOS image" under Qemu
+ */
+
+#define _GNU_SOURCE
+#include <elf.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <paths.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include "../include16/sys16.h"
+
+/*
+ * The program (toybox) is loaded at 0xe0000 and then copied down to
+ * 0x10000 to be in "RAM"; the wrapper_data is loaded at 0xf0000 and
+ * contains anything needed to set up the early execution environment.
+ */
+#define TOYBOX_SIZE  65536
+#define WRAPPER_SIZE 65536
+
+static unsigned char *toybox, *wrapper_data;
+static int toyprot[TOYBOX_SIZE/4096];
+extern const unsigned char wrapper[];
+extern const unsigned int wrapper_len;
+
+#define ALIGN_UP(x,y) (((x) + (y) - 1) & ~((y) - 1))
+
+static void init_page_tables(void)
+{
+	int i;
+	uint64_t *page_tables = (uint64_t *)(wrapper_data + 0x2000);
+
+	/* Map 256 pages = 1MiB */
+	for (i = 0; i < 1024/4; i++) {
+		/* Page present, RWX, system */
+		page_tables[i] = (i << 12) | 0x063;
+	}
+
+	/* Map 64-128K as user pages with appropriate permissions */
+	for (i = 64/4; i < 128/4; i++) {
+		int tp = toyprot[i-64/4];
+		page_tables[i] = (i << 12) | 0x064
+			| ((tp & PF_X) ? 0 : (UINT64_C(1) << 63))
+			| ((tp & PF_W) ? 2 : 0)
+			| ((tp & (PF_X|PF_W|PF_R)) ? 1 : 0);
+	}
+
+	/* Set up PDPTR */
+	*(uint64_t *)(wrapper_data + 0x0000) = 0xf1000 | 1;
+
+	/* Set up PDE */
+	*(uint64_t *)(wrapper_data + 0x1000) = 0xf2000 | 0x027;
+}
+
+static void init_reset_vector(void)
+{
+	/* jmp far 0xf000:0x8000 */
+	memcpy(wrapper_data + 0xfff0, "\xea\x00\x80\x00\xf0", 5);
+}
+
+static void init_wrapper(void)
+{
+	/* Fixed wrapper data at 0xf000:0x8000 */
+	memcpy(wrapper_data + 0x8000, wrapper, wrapper_len);
+
+	init_page_tables();
+
+	init_reset_vector();
+}
+
+static int load_elf(const char *file, uint32_t *start)
+{
+	int fd;
+	struct stat st;
+	const char *fp;		/* File data pointer */
+	const Elf32_Ehdr *eh;
+	const Elf32_Phdr *ph;
+	int rv = -1;
+	int i;
+
+	fd = open(file, O_RDONLY);
+	if (fd < 0 || fstat(fd, &st))
+		goto bail;
+
+	fp = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
+	if (fp == MAP_FAILED)
+		goto bail;
+
+	errno = ENOEXEC;	/* If no other error... */
+
+	/* Must be long enough */
+	if (st.st_size < sizeof(Elf32_Ehdr))
+		goto bail;
+
+	eh = (const Elf32_Ehdr *)fp;
+
+	/* Must be ELF, 32-bit, littleendian, version 1 */
+	if (memcmp(eh->e_ident, "\x7f" "ELF\1\1\1", 6))
+		goto bail;
+
+	if (eh->e_machine != EM_386)
+		goto bail;
+
+	if (eh->e_version != EV_CURRENT)
+		goto bail;
+
+	if (eh->e_ehsize < sizeof(Elf32_Ehdr) || eh->e_ehsize >= st.st_size)
+		goto bail;
+
+	if (eh->e_phentsize < sizeof(Elf32_Phdr))
+		goto bail;
+
+	if (!eh->e_phnum)
+		goto bail;
+
+	if (eh->e_phoff + eh->e_phentsize * eh->e_phnum > st.st_size)
+		goto bail;
+
+	if (eh->e_entry > TOYBOX_SIZE)
+		goto bail;
+
+	*start = eh->e_entry;
+
+	ph = (const Elf32_Phdr *)(fp + eh->e_phoff);
+
+	for (i = 0; i < eh->e_phnum; i++) {
+		uint32_t addr = ph->p_paddr;
+		uint32_t msize = ph->p_memsz;
+		uint32_t dsize = ph->p_filesz;
+		int page, flags;
+
+		ph = (const Elf32_Phdr *)((const char *)ph +
+					  i*eh->e_phentsize);
+
+		if (msize && (ph->p_type == PT_LOAD ||
+			      ph->p_type == PT_PHDR)) {
+			/*
+			 * This loads at p_paddr, which is arguably
+			 * the correct semantics (LMA).  The SysV spec
+			 * says that SysV loads at p_vaddr (and thus
+			 * Linux does, too); that is, however, a major
+			 * brainfuckage in the spec.
+			 */
+			if (msize < dsize)
+				dsize = msize;
+
+			if (addr >= TOYBOX_SIZE ||
+			    addr + msize > TOYBOX_SIZE)
+				goto bail;
+
+			if (dsize) {
+				if (ph->p_offset >= st.st_size ||
+				    ph->p_offset + dsize > st.st_size)
+					goto bail;
+				memcpy(toybox + addr, fp + ph->p_offset,
+				       dsize);
+			}
+
+			memset(toybox + addr + dsize, 0, msize - dsize);
+
+			flags = ph->p_flags & (PF_X|PF_W|PF_R);
+			for (page = addr & ~0xfff; page < addr + msize;
+			     page += 4096)
+				toyprot[page >> 12] |= flags;
+		}
+	}
+
+	rv = 0;			/* Success! */
+
+bail:
+	if (fd >= 0)
+		close(fd);
+	return rv;
+}
+
+static void build_env(const char *file, char **argv)
+{
+	uint32_t start;
+	struct system_struct *SYS;
+	char **argp;
+	uint32_t argptr, argstr;
+
+	/* Read the input file */
+	if (load_elf(file, &start))
+		goto barf;
+
+	/* Set up the system structure */
+	SYS = (struct system_struct *)(toybox + SYS_STRUCT_ADDR);
+	SYS->entrypoint = start;
+	for (argp = argv; *argp; argp++)
+		SYS->argc++;
+
+	argptr = ALIGN_UP(SYS_STRUCT_ADDR + sizeof(struct system_struct), 4);
+	argstr = argptr + (SYS->argc + 1)*sizeof(uint32_t);
+
+	SYS->argv = argptr;
+
+	for (argp = argv; *argp; argp++) {
+		int len = strlen(*argp)+1;
+
+		if (argstr + len >= TOYBOX_SIZE) {
+			fprintf(stderr, "run16: %s: command line too long\n",
+				file);
+			exit(127);
+		}
+
+		*(uint32_t *)(toybox + argptr) = argstr;
+		argptr += sizeof(uint32_t);
+		memcpy(toybox + argstr, *argp, len);
+		argstr += len;
+	}
+
+	/* Set up page protection */
+	toyprot[0xE] |= PF_R | PF_W;	/* Stack page */
+	toyprot[0xF] |= PF_R;		/* System page */
+
+	init_wrapper();
+	return;
+
+barf:
+	fprintf(stderr, "run16: %s: %s\n", file, strerror(errno));
+	exit(127);
+}
+
+static int run(const char *qemu, const char *file, char **argv)
+{
+	const char *tmpdir, *et;
+	char *filename;
+	int fd, status, err;
+	pid_t p;
+
+	tmpdir = getenv("TMPDIR");
+	if (!tmpdir)
+		tmpdir = _PATH_TMP;
+
+	et = strchr(tmpdir, '\0');
+	if (asprintf(&filename, "%s%srun16.tmp.XXXXXX", tmpdir,
+		     (et == tmpdir || et[-1] == '/') ? "" : "/") < 0)
+		goto barf_no_rm;
+	fd = mkstemp(filename);
+	if (fd < 0)
+		goto barf_no_rm;
+
+	if (ftruncate(fd, TOYBOX_SIZE + WRAPPER_SIZE))
+		goto barf;
+
+	toybox = mmap(NULL, TOYBOX_SIZE + WRAPPER_SIZE, PROT_READ|PROT_WRITE,
+		      MAP_SHARED, fd, 0);
+	if (toybox == MAP_FAILED)
+		goto barf;
+
+	wrapper_data = toybox + TOYBOX_SIZE;
+
+	build_env(file, argv);
+
+	if (munmap(toybox, TOYBOX_SIZE + WRAPPER_SIZE))
+		goto barf;
+	if (close(fd))
+		goto barf;
+
+	fflush(NULL);
+
+	p = fork();
+	if (p < 0)
+		goto barf;
+
+	if (p == 0) {
+		execlp(qemu, qemu, "-cpu", "qemu32,+xd",
+		       "-nodefaults", "-nodefconfig",
+		       "-m", "1", "-display", "none", "-vga", "none",
+		       "-debugcon", "stdio", "-device", "isa-debug-exit",
+		       "-bios", filename, (char *)NULL);
+		_exit(127);
+	}
+
+	while (waitpid(p, &status, 0) != p)
+		;
+
+	unlink(filename);
+
+	return (WIFEXITED(status) && WEXITSTATUS(status) == (('S' << 1) | 1)
+		? 0 : 1);
+
+barf:
+	err = errno;
+	unlink(filename);
+	errno = err;
+barf_no_rm:
+	fprintf(stderr, "run16: %s: %s\n", file, strerror(errno));
+	exit(127);
+}
+
+int main(int argc, char *argv[])
+{
+	const char *qemu = "qemu-system-i386";
+
+	if (argc < 2) {
+		fprintf(stderr, "Usage: %s filename [args...]\n", argv[0]);
+		return 127;
+	}
+
+	return run(qemu, argv[1], argv+1);
+}
diff --git a/gcc/testsuite/gcc.target/i386/m16/run/wrap.S b/gcc/testsuite/gcc.target/i386/m16/run/wrap.S
new file mode 100644
index 0000000..b9ba1af
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/m16/run/wrap.S
@@ -0,0 +1,180 @@
+/*
+ * Code wrapper: set up a 16-bit protected mode environment with
+ * paging enabled, so we can catch permission violations.
+ *
+ * This will be combined with the target program and fed as a single
+ * 128K binary "BIOS" image to Qemu, which will load it at 0xe0000.
+ *
+ * The first 64K is the actual program, then immediately follows
+ * PAE page tables set up so the low 1M is identity-mapped, with
+ * 64K-128K as user mode with appropriate permissions and the balance
+ * as RWX supervisor.
+ *
+ * This chunk is then loaded at 32K, and finally the reset vector at
+ * -16 bytes with a jump to f000:8000.
+ */
+	.code16
+	.text
+	.globl _start
+_start:
+	cld
+
+	/* Copy the program code to 0x10000, in Qemu "RAM" */
+	movw $0xe000,%ax
+	movw %ax,%ds
+	movw $0x1000,%ax
+	movw %ax,%es
+	movw $0x4000,%cx
+	rep; movsl
+
+	movw %cs,%ax
+	movw %ax,%ds
+	xorw %ax,%ax
+	movw %ax,%ss
+	movl $0xfff0,%esp
+	movw %ax,%es
+
+	/* Set up GDT at 0:0x1000, followed by TSS */
+	movw $gdt_master,%si
+	movw $0x1000,%di
+	movw $(gdt_len + 104),%cx
+	shrw $2,%cx
+	rep; movsl
+
+	lgdtl gdt_desc
+	lidtl idt_desc
+
+	movl $0x30,%eax		/* Enable PAE and PSE */
+	movl %eax,%cr4
+
+	movl $0xc0000080,%ecx	/* IA32_EFER */
+	rdmsr
+	orl $(1 << 11),%eax	/* Enable NX */
+	wrmsr
+
+	movl $0xf0000,%eax	/* Page table base */
+	movl %eax,%cr3
+
+	movw $0x10,%dx		/* Data segment */
+	movw $0x18,%cx		/* Stack segment */
+	movw $0x30,%bx		/* TSS segment */
+	xorw %si,%si		/* Null selector */
+
+	movl $0x80010033,%eax
+	movl %eax,%cr0
+	ljmpw $0x08,$1f
+1:
+	movw %cx,%ss
+	movw %dx,%ds
+	movw %dx,%es
+	movw %si,%fs		/* fs/gs should never be implicitly accessed */
+	movw %si,%gs
+	ltr %bx
+
+	movw $0x2b,%dx		/* User data segment */
+	pushw %dx		/* User SS */
+	pushw $0xf000		/* User SP */
+	pushw $0x3002		/* User FLAGS (IOPL=3) */
+	pushw $0x23		/* User CS */
+	movw %dx,%ds
+	pushw 0xf000		/* sysseg:0xf000 holds the user IP */
+	movw %dx,%es
+	iretw			/* Go to user program */
+
+	.balign 16
+trap_handler:
+	movb $'T',%al			/* T = trap */
+	movw $0x501,%dx
+	outb %al,%dx
+	/* This should kill Qemu */
+die:
+	hlt
+	jmp die
+
+	.section ".rodata","a"
+	.balign 16
+gdt_desc:
+	.word gdt_len
+	.long 0x1000
+	.word 0
+
+gdt_master:
+	.long 0,0
+
+	/* Segment 0x08: 16-bit system code segment (in ROM) */
+	.word 0xffff			/* Limit[15:0] */
+	.long 0x0f0000 + 0x9b000000	/* Base[24:0] + code segment */
+	.word 0x0000			/* Base[31:24] + 16-bit segment */
+
+	/* Segment 0x10: 16-bit system data segment (in ROM) */
+	.word 0xffff			/* Limit[15:0] */
+	.long 0x0f0000 + 0x93000000	/* Base[24:0] + code segment */
+	.word 0x0000			/* Base[31:24] + 16-bit segment */
+
+	/* Segment 0x18: 16-bit system stack segment (in low memory) */
+	.word 0xffff			/* Limit[15:0] */
+	.long 0x000000 + 0x93000000	/* Base[24:0] + code segment */
+	.word 0x0000			/* Base[31:24] + 16-bit segment */
+
+	/* Segment 0x23: 16-bit user code segment */
+	.word 0xffff			/* Limit[15:0] */
+	.long 0x010000 + 0xfb000000	/* Base[24:0] + code segment */
+	.word 0x0000			/* Base[31:24] + 16-bit segment */
+
+	/* Segment 0x2b: 16-bit user data segment */
+	.word 0xffff			/* Limit[15:0] */
+	.long 0x010000 + 0xf3000000	/* Base[24:0] + code segment */
+	.word 0x0000			/* Base[31:24] + 16-bit segment */
+
+	/* Segment 0x30: TSS */
+	.word 104			/* Limit */
+	.long (0x001000	+ gdt_len) + 0x89000000
+					/* Base[15:0] + available TSS */
+	.word 0x0000			/* Base[31:24] + limit[19:16] */
+
+gdt_len	= . - gdt_master
+
+	/* Must immediately follow the GDT */
+tss_master:
+	.long 0				/* Task link */
+	.long 0				/* ESP0 */
+	.long 0x18			/* SS0 */
+	.long 0				/* ESP1 */
+	.long 0				/* SS1 */
+	.long 0				/* ESP2 */
+	.long 0				/* SS2 */
+	.long 0xf0000			/* CR3 */
+	.long 0				/* EIP */
+	.long 0				/* EFLAGS */
+	.long 0				/* EAX */
+	.long 0				/* ECX */
+	.long 0				/* EDX */
+	.long 0				/* EBX */
+	.long 0				/* ESP */
+	.long 0				/* EBP */
+	.long 0				/* ESI */
+	.long 0				/* EDI */
+	.long 0				/* ES */
+	.long 0				/* CS */
+	.long 0				/* SS */
+	.long 0				/* DS */
+	.long 0				/* FS */
+	.long 0				/* GS */
+	.long 0				/* LDTR */
+	.long 0xffff0000		/* T + IObase */
+
+idt_desc:
+	.word idt_len
+	.long idt + 0xf0000
+	.word 0
+
+	.balign 16
+idt:
+	.rept 256
+	.word trap_handler		/* Offset[15:0] */
+	.word 0x08			/* Code segment */
+	.word 0x8600			/* Interrupt gate, DPL 0 */
+	.word 0				/* Offset[31:16] */
+	.endr
+
+idt_len = . - idt
diff --git a/gcc/testsuite/gcc.target/i386/m16/run/wwrap.S b/gcc/testsuite/gcc.target/i386/m16/run/wwrap.S
new file mode 100644
index 0000000..3dfe5d6
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/m16/run/wwrap.S
@@ -0,0 +1,13 @@
+	.section ".rodata","a"
+	.globl wrapper
+wrapper:
+	.incbin "run/wrap.bin"
+	.balign 16
+	.size wrapper,.-wrapper
+
+	.globl wrapper_len
+wrapper_len:
+	.long .-wrapper
+	.size wrapper_len,.-wrapper_len
+
+	.section .note.GNU-stack,"",@progbits
diff --git a/gcc/testsuite/gcc.target/i386/m16/test16/test1.c b/gcc/testsuite/gcc.target/i386/m16/test16/test1.c
new file mode 100644
index 0000000..b79e7be
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/m16/test16/test1.c
@@ -0,0 +1,7 @@
+extern void puts(const char *);
+
+int main(void)
+{
+	puts("Hello, World!\n");
+	return 0;
+}
-- 
1.8.5.3

Reply via email to