/*
 * Copyright (c) 2000 Silicon Graphics, Inc.  All Rights Reserved.
 * Portions Copyright (c) 2000 Ulrich Drepper
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of version 2 of the GNU General Public License as
 * published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it would be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 *
 * Further, this software is distributed without any warranty that it is
 * free of the rightful claim of any third person regarding infringement
 * or the like.  Any license provided herein, whether implied or
 * otherwise, applies only to this software file.  Patent licenses, if
 * any, provided herein do not apply to combinations of this program with
 * other software, or any other product whatsoever.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
 * Mountain View, CA  94043, or:
 *
 * http://www.sgi.com$
 *
 * For further information regarding this notice, see:$
 *
 * http://oss.sgi.com/projects/GenInfo/NoticeExplan/
 *
 *
 *    Linux Test Project - Silicon Graphics, Inc.
 *    TEST IDENTIFIER	: fork05
 *    EXECUTED BY	: anyone
 *    TEST TITLE	: Make sure LDT is propagated correctly
 *    TEST CASE TOTAL	: 1
 *    CPU TYPES		: i386
 *    AUTHORS		: Ulrich Drepper
 *			  Nate Straz
 *
 *On Friday, May 2, 2003 at 09:47:00AM MST, Ulrich Drepper wrote:
 *>Robert Williamson wrote:
 *>
 *>>   I'm getting a SIGSEGV with one of our tests, fork05.c, that apparently
 *>> you wrote (attached below).  The test passes on my 2.5.68 machine running
 *>> SuSE 8.0 (glibc 2.2.5 and Linuxthreads), however it segmentation faults on
 *>> RedHat 9 running 2.5.68.  The test seems to "break" when it attempts to run
 *>> the assembly code....could you take a look at it?
 *>
 *>There is no need to look at it, I know it cannot work anymore on recent
 *>systems.  Either change all uses of %gs to %fs or skip the entire patch
 *>if %gs has a nonzero value.
 *>
 *>- --
 *>- --------------.                        ,-.            444 Castro Street
 *>Ulrich Drepper \    ,-----------------'   \ Mountain View, CA 94041 USA
 *>Red Hat         `--' drepper at redhat.com `---------------------------
 *
 *
 *
 *On Sat, Aug 12, 2000 at 12:47:31PM -0700, Ulrich Drepper wrote:
 *> Ever since the %gs handling was fixed in the 2.3.99 series the
 *> appended test program worked.  Now with 2.4.0-test6 it's not working
 *> again.  Looking briefly over the patch from test5 to test6 I haven't
 *> seen an immediate candidate for the breakage.  It could be missing
 *> propagation of the LDT to the new process (and therefore an invalid
 *> segment descriptor) or simply clearing %gs.
 *>
 *> Anyway, this is what you should see and what you get with test5:
 *>
 *> ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 *> a = 42
 *> %gs = 0x0007
 *> %gs = 0x0007
 *> a = 99
 *> ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 *>
 *> This is what you get with test6:
 *>
 *> ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 *> a = 42
 *> %gs = 0x0007
 *> %gs = 0x0000
 *> <SEGFAULT>
 *> ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 *>
 *> If somebody is actually creating a test suite for the kernel, please
 *> add this program.  It's mostly self-contained.  The correct handling
 *> of %gs is really important since glibc 2.2 will make heavy use of it.
 *>
 *> - --
 *> - ---------------.                          ,-.   1325 Chesapeake Terrace
 *> Ulrich Drepper  \    ,-------------------'   \  Sunnyvale, CA 94089 USA
 *> Red Hat          `--' drepper at redhat.com   `------------------------
 *>
 *> ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 *
 */

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#include "lapi/syscalls.h"
#include "test.h"

char *TCID = "fork05";

static char *environ_list[] = { "TERM", "NoTSetzWq", "TESTPROG" };

#define NUMBER_OF_ENVIRON (sizeof(environ_list)/sizeof(char *))
int TST_TOTAL = NUMBER_OF_ENVIRON;

#if defined(linux) && defined(__i386__)

struct modify_ldt_ldt_s {
	unsigned int entry_number;
	unsigned long int base_addr;
	unsigned int limit;
	unsigned int seg_32bit:1;
	unsigned int contents:2;
	unsigned int read_exec_only:1;
	unsigned int limit_in_pages:1;
	unsigned int seg_not_present:1;
	unsigned int useable:1;
	unsigned int empty:25;
};

static int a = 42;

static void modify_ldt(int func, struct modify_ldt_ldt_s *ptr, int bytecount)
{
	tst_syscall(__NR_modify_ldt, func, ptr, bytecount);
}

int main(void)
{
	struct modify_ldt_ldt_s ldt0;
	int lo;
	pid_t pid;
	int res;

	ldt0.entry_number = 0;
	ldt0.base_addr = (long)&a;
	ldt0.limit = 4;
	ldt0.seg_32bit = 1;
	ldt0.contents = 0;
	ldt0.read_exec_only = 0;
	ldt0.limit_in_pages = 0;
	ldt0.seg_not_present = 0;
	ldt0.useable = 1;
	ldt0.empty = 0;

	modify_ldt(1, &ldt0, sizeof(ldt0));

	asm volatile ("movw %w0, %%fs"::"q" (7));

	asm volatile ("movl %%fs:0, %0":"=r" (lo));
	tst_resm(TINFO, "a = %d", lo);

	asm volatile ("pushl %%fs; popl %0":"=q" (lo));
	tst_resm(TINFO, "%%fs = %#06hx", lo);

	asm volatile ("movl %0, %%fs:0"::"r" (99));

	pid = fork();

	if (pid == 0) {
		asm volatile ("pushl %%fs; popl %0":"=q" (lo));
		tst_resm(TINFO, "%%fs = %#06hx", lo);

		asm volatile ("movl %%fs:0, %0":"=r" (lo));
		tst_resm(TINFO, "a = %d", lo);

		if (lo != 99)
			tst_resm(TFAIL, "Test failed");
		else
			tst_resm(TPASS, "Test passed");
		exit(lo != 99);
	} else {
		waitpid(pid, &res, 0);
	}

	return WIFSIGNALED(res);
}

#else /* if defined(linux) && defined(__i386__) */

int main(void)
{
	tst_resm(TINFO, "%%fs test only for ix86");

	/*
	 * should be successful on all non-ix86 platforms.
	 */
	tst_exit();
}

#endif /* if defined(linux) && defined(__i386__) */
