#include <string.h>
#include <stddef.h>
#include <stdio.h>
#include <signal.h>

#include <asm/types.h>
#include <linux/module.h> 
#include <asm/unistd.h>

void setam(void), setam_end(void); 
asm(" 
setam: 
	movl %cr0,%eax
	orl  $1<<18,%eax
	movl %eax,%cr0
	movl $-999,%eax
	ret
setam_end:
"); 

// set the system global alignment exception flag in CR0 
// needs to run once per system boot as root
int enable_am(void) 
{ 
	unsigned long base;
	struct custommodule { 
		struct module m;
		char name[16]; 
		char code[32]; 
	} mod;
	int n; 

	// execute a piece of code in kernel mode
	memset(&mod.m,0,sizeof(struct module)); 
	memcpy(&mod.code, (char *) setam, (char *)setam_end - (char*)setam); 
	mod.m.size_of_struct = sizeof(struct module);
	mod.m.size = sizeof mod; 
	strcpy(mod.name, "ammodule");	
	base = create_module(mod.name, sizeof mod); 
	if ((long)base < 0 && (long)base > -2000) 
		return -1; 
	mod.m.init = (void *) (base + offsetof(struct custommodule, code)); 
	mod.m.name = (char *) (offsetof(struct custommodule, name) + base);
        n = init_module(mod.name, &mod); 
	if (n == -999) 
		return 0; 
	delete_module("ammodule"); 
	return 0; 
} 

void alignment(int sig, struct sigcontext ctx) 
{ 
	if (ctx.trapno != 17) { 
		printf("signal %d\n"); 
		exit(1); 
	} 

	printf("got %d alignment from %lx\n", sig, ctx.eip); 
	exit(1); 
}


int main()
{ 
	// system global
	if (enable_am() < 0) perror("enable_am"), exit(1); 	

	// enable it for the current process
	asm("pushfl ; orl $1<<18,(%esp) ; popfl"); 
	
	// test it 
	{ 
		long var; 
		signal(SIGBUS, (__sighandler_t) alignment);   // linux 2.4 sends SIGBUS
		signal(SIGSEGV, (__sighandler_t) alignment);  // and 2.2 SIGSEGV
		*(long *)(((unsigned long)&var) + 1) = 100; 
	} 


} 
