[Fault-injection-developer] [PATCH 4/4] pagefault interceptor modules patch against 2.5.47 + kprobe
Status: Alpha
Brought to you by:
rustyl
From: Zhuang, L. <lou...@in...> - 2002-11-25 10:18:24
|
The pagefault interceptor can catch MMIO access by placing 'page absent bit' in PTE and handle the pagefault execption. FITH user space utility can be downloaded in http://prdownloads.sourceforge. net/fault-injection/fith-mini-8.tgz?download diff -Nur -X dontdiff linux-2.5-kp-fi-dm/linux-2.5.47/arch/i386/Kconfig linux-2.5-kp-fi-dm-intcpts/linux-2.5.47/arch/i386/Kconfig --- linux-2.5-kp-fi-dm/linux-2.5.47/arch/i386/Kconfig Mon Nov 25 15:42:12 2002 +++ linux-2.5-kp-fi-dm-intcpts/linux-2.5.47/arch/i386/Kconfig Mon Nov 25 17:06:12 2002 @@ -1584,6 +1584,12 @@ Only support M here. It is a bug to be fixed. Decision Maker is to control interceptors how/what to inject. +config FI_PF + tristate "Pagefault interceptor" + depends on FI_DM + help + The interceptor can catch MMIO access by page-absent execption. + config DEBUG_STACKOVERFLOW bool "Check for stack overflows" depends on DEBUG_KERNEL diff -Nur -X dontdiff linux-2.5-kp-fi-dm/linux-2.5.47/arch/i386/kernel/Makefile linux-2.5-kp-fi-dm-intcpts/linux-2.5.47/arch/i386/kernel/Makefile --- linux-2.5-kp-fi-dm/linux-2.5.47/arch/i386/kernel/Makefile Mon Nov 25 15:42:12 2002 +++ linux-2.5-kp-fi-dm-intcpts/linux-2.5.47/arch/i386/kernel/Makefile Mon Nov 25 17:06:12 2002 @@ -12,6 +12,7 @@ obj-y += cpu/ obj-y += timers/ +obj-y += fi/ obj-$(CONFIG_X86_BIOS_REBOOT) += reboot.o obj-$(CONFIG_MCA) += mca.o obj-$(CONFIG_X86_MSR) += msr.o diff -Nur -X dontdiff linux-2.5-kp-fi-dm/linux-2.5.47/arch/i386/kernel/fi/Makefile linux-2.5-kp-fi-dm-intcpts/linux-2.5.47/arch/i386/kernel/fi/Makefile --- linux-2.5-kp-fi-dm/linux-2.5.47/arch/i386/kernel/fi/Makefile Thu Jan 1 08:00:00 1970 +++ linux-2.5-kp-fi-dm-intcpts/linux-2.5.47/arch/i386/kernel/fi/Makefile Mon Nov 25 17:06:12 2002 @@ -0,0 +1,10 @@ +# +# Makefile for the linux kernel. +# + +obj-y += pf/ +obj-y += aslt/ + +EXTRA_AFLAGS := -traditional + +include $(TOPDIR)/Rules.make diff -Nur -X dontdiff linux-2.5-kp-fi-dm/linux-2.5.47/arch/i386/kernel/fi/aslt/Makefile linux-2.5-kp-fi-dm-intcpts/linux-2.5.47/arch/i386/kernel/fi/aslt/Makefile --- linux-2.5-kp-fi-dm/linux-2.5.47/arch/i386/kernel/fi/aslt/Makefile Thu Jan 1 08:00:00 1970 +++ linux-2.5-kp-fi-dm-intcpts/linux-2.5.47/arch/i386/kernel/fi/aslt/Makefile Mon Nov 25 17:18:49 2002 @@ -0,0 +1,10 @@ +# +# Makefile for the linux kernel. +# +export-objs := fi_aslt.o +obj-$(CONFIG_FI_PF) += fi_aslt.o + + +EXTRA_AFLAGS := -traditional + +include $(TOPDIR)/Rules.make diff -Nur -X dontdiff linux-2.5-kp-fi-dm/linux-2.5.47/arch/i386/kernel/fi/aslt/fi_aslt.c linux-2.5-kp-fi-dm-intcpts/linux-2.5.47/arch/i386/kernel/fi/aslt/fi_aslt.c --- linux-2.5-kp-fi-dm/linux-2.5.47/arch/i386/kernel/fi/aslt/fi_aslt.c Thu Jan 1 08:00:00 1970 +++ linux-2.5-kp-fi-dm-intcpts/linux-2.5.47/arch/i386/kernel/fi/aslt/fi_aslt.c Mon Nov 25 17:18:49 2002 @@ -0,0 +1,282 @@ +/************************************************************************** **** + * Fault Injection Test harness (FI) + * Copyright (C) Intel Crop. + * + * 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 2 + * 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + **************************************************************************** ** + */ + +/* $Id: aslt.c,v 1.3 2002/11/14 07:11:40 brlock Exp $ + * Copyright by Intel Crop., 2002 + * Stanley Wang (sta...@in...) + */ + +/** + * @file aslt.c + */ + +#include <linux/config.h> +#include <linux/version.h> + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/signal.h> +#include <linux/sched.h> +#include <linux/interrupt.h> +#include <linux/string.h> +#include <linux/ioport.h> +#include <linux/mm.h> +#include <linux/slab.h> +#include <asm/io.h> +#include <linux/highmem.h> +#include <asm/pgtable.h> + + +#define PREFIX_NAME "FI_ASLT" +#include <linux/fi/fi.h> +#include <linux/fi/fi_interface.h> +#include <linux/fi/fi_internal.h> + +#define COMPONENT_NAME "fi.interceptor.aslt" +#define INTERFACE_NAME COMPONENT_NAME ".iIntercept" +#define LINE_TO_PHY COMPONENT_NAME ".iLine_to_phy" + +#define PF_COMPONENT_NAME "fi.interceptor.pf" +#define PF_INTERFACE_NAME PF_COMPONENT_NAME ".iIntercept" +#define PF_INTERCEPTOR_MODULE "fi_pf" + +#define MAX_WP 32 + +long globalcounter=0; +int aslt_register(struct watchpoint wp, int id, struct iInterceptHook *hook, char *modname); +int aslt_unregister(int id, char *modname); +void * fi_ioremap(unsigned long phys_addr, unsigned long size, unsigned long flags); +unsigned long fi_line_to_phy(unsigned long line_addr); +unsigned long fi_phy_to_line(unsigned long phy_addr); +int check_wp(struct watchpoint wp); + +/* Store all wps */ +static struct{ + struct watchpoint wp; + int id; + struct iInterceptHook * hook; + char modname[256]; +}wp_tb[MAX_WP] = { [0 ... MAX_WP-1] = { + wp: {0,0}, + id: -1, + hook: NULL } +}; + +static struct iIntercept intcpt[1] = { + { + wp_register: aslt_register, + wp_unregister: aslt_unregister, + corrupt: NULL, + } +}; + +static struct iAslt aslt[1] = { + { + fi_line_to_phy: fi_line_to_phy, + check_wp: check_wp, + } +}; + +static struct iIntercept *pf_intcpt=NULL; + +inline int find_wp(const void *addr, unsigned long len){ + int i=0; + + for (i=0; i<MAX_WP; i++) { + if (wp_tb[i].hook == NULL) continue; + if ( wp_tb[i].wp.addr >= addr && wp_tb[i].wp.addr < addr + len ) { + break; + } + } + return i; +}; + + +void * fi_ioremap(unsigned long phys_addr, unsigned long size, unsigned long flags) +{ + void *line_addr; + int idx; + int rv; + unsigned long offset; + + line_addr = __ioremap(phys_addr, size, flags); + + PDEBUG("Linear address: %#lX\n", (unsigned long)line_addr); + + while ((idx = find_wp((void *)phys_addr, size)) < MAX_WP){ + offset = (unsigned long)(wp_tb[idx].wp.addr) - phys_addr; + wp_tb[idx].wp.addr = (void *)((unsigned long)line_addr + offset); + rv = pf_intcpt->wp_register(wp_tb[idx].wp, wp_tb[idx].id, wp_tb[idx].hook, wp_tb[idx].modname); + if (rv < 0) { + PERROR("Register fault set fail! @%s\n", __FILE__); + } + wp_tb[idx].hook = NULL; + wp_tb[idx].id = -1; + wp_tb[idx].wp.addr = 0; + wp_tb[idx].wp.type = 0; + (wp_tb[idx].modname)[0] = '\0'; + } + return line_addr; +} + +unsigned long fi_line_to_phy(unsigned long line_addr) { + pgd_t *pgd; + pmd_t *pmd; + pte_t *pte; + unsigned long phy_addr; + + pgd = pgd_offset_k(line_addr); + if ( !(pgd_val(*pgd)&_PAGE_PRESENT) ) { + PERROR("Linear address is not present! @%s\n", __FILE__); + return 0; + }; + pmd = pmd_offset(pgd, line_addr); + if (pmd_large(*pmd)) { + pte = (pte_t *)pmd; + } else { + pte = pte_offset_kernel(pmd, line_addr); + } + phy_addr = (pte_val(*pte)&PAGE_MASK) | (line_addr&(~PAGE_MASK)); + return phy_addr; +}; + +unsigned long fi_phy_to_line(unsigned long phy_addr) { + pgd_t *pgd; + pmd_t *pmd; + pte_t *pte; + unsigned long line_addr; + + for (line_addr=VMALLOC_START; line_addr<VMALLOC_END; + line_addr+=PAGE_SIZE ) { + pgd = pgd_offset_k(line_addr); + if ( !(pgd_val(*pgd)&_PAGE_PRESENT) ) { + continue; + }; + pmd = pmd_offset(pgd, line_addr); + if (pmd_large(*pmd)) { + pte = (pte_t *)pmd; + } else { + pte = pte_offset_kernel(pmd, line_addr); + } + if ( (pte_val(*pte)&PAGE_MASK) == (phy_addr&PAGE_MASK) ) { + return line_addr + (phy_addr&(~PAGE_MASK)); + }; + } + return 0; //can not find the line address +}; + +int check_wp(struct watchpoint wp) +{ + int idx; + + idx = find_wp(wp.addr, 1); + if (idx < MAX_WP) { + return 1; /*found*/ + } + return 0; +} + +int aslt_register(struct watchpoint wp, int id, struct iInterceptHook *hook, char *modname) +{ + void *line_addr; + int idx; + + line_addr = (void *)fi_phy_to_line((unsigned long)(wp.addr)); + if (line_addr != 0) { + wp.addr = line_addr; + return pf_intcpt->wp_register(wp, id, hook, modname); + } else { + PDEBUG("Address is not present, delay register until \"insmod\"! @%s\n", __FILE__); + idx = find_wp(wp.addr, 1); + if (idx < MAX_WP) { + PDEBUG("Address is duplicated, fail! @%s\n", __FILE__); + return -EFI_INVALID; + } + for (idx=0; idx<MAX_WP; idx++){ + if (wp_tb[idx].hook == NULL){ + break; + } + } + if (idx>=MAX_WP) return -EFI_TABFULL; + + wp_tb[idx].hook = hook; + wp_tb[idx].id = id; + wp_tb[idx].wp.addr = wp.addr; + wp_tb[idx].wp.type = wp.type; + if (modname != NULL) + strncpy(wp_tb[idx].modname, modname, sizeof(wp_tb[idx].modname)); + } + return EFI_SUCCESS; +} + +int aslt_unregister (int id, char *modname) +{ + int idx; + + for (idx=0;idx<MAX_WP;idx++) { + if (wp_tb[idx].id == id) + break; + } + if (idx >= MAX_WP) + return pf_intcpt->wp_unregister(id, modname); + + wp_tb[idx].hook = NULL; + wp_tb[idx].id = -1; + wp_tb[idx].wp.addr = 0; + wp_tb[idx].wp.type = 0; + return EFI_SUCCESS; +} + +static int __init aslt_start(void) +{ + pf_intcpt = (struct iIntercept *)inter_module_get_request(PF_INTERFACE_NAME, PF_INTERCEPTOR_MODULE); + if (pf_intcpt != NULL) { +// intcpt[0].wp_unregister = pf_intcpt->wp_unregister; + intcpt[0].corrupt = pf_intcpt->corrupt; + inter_module_register(INTERFACE_NAME, THIS_MODULE, intcpt); + } else { + PERROR("Can not get interface %s!\n", PF_INTERFACE_NAME); + return -1; + } + + inter_module_register(LINE_TO_PHY, THIS_MODULE, aslt); + + return 0; +} + +static void __exit aslt_stop(void) +{ + inter_module_unregister(LINE_TO_PHY); + inter_module_unregister(INTERFACE_NAME); + inter_module_put(PF_INTERFACE_NAME); +} + +module_init(aslt_start); +module_exit(aslt_stop); + +EXPORT_SYMBOL_NOVERS(fi_ioremap); + +MODULE_AUTHOR("Stanley Wang"); +MODULE_DESCRIPTION("ASLT component for FI."); +MODULE_LICENSE("GPL"); + diff -Nur -X dontdiff linux-2.5-kp-fi-dm/linux-2.5.47/arch/i386/kernel/fi/pf/Makefile linux-2.5-kp-fi-dm-intcpts/linux-2.5.47/arch/i386/kernel/fi/pf/Makefile --- linux-2.5-kp-fi-dm/linux-2.5.47/arch/i386/kernel/fi/pf/Makefile Thu Jan 1 08:00:00 1970 +++ linux-2.5-kp-fi-dm-intcpts/linux-2.5.47/arch/i386/kernel/fi/pf/Makefile Mon Nov 25 17:06:12 2002 @@ -0,0 +1,11 @@ +# +# Makefile for the linux kernel. +# + +obj-$(CONFIG_FI_PF) += fi_pf.o +fi_pf-objs := pf_interface.o pf.o pf_in.o pf_region.o + + +EXTRA_AFLAGS := -traditional + +include $(TOPDIR)/Rules.make diff -Nur -X dontdiff linux-2.5-kp-fi-dm/linux-2.5.47/arch/i386/kernel/fi/pf/fi_pf.h linux-2.5-kp-fi-dm-intcpts/linux-2.5.47/arch/i386/kernel/fi/pf/fi_pf.h --- linux-2.5-kp-fi-dm/linux-2.5.47/arch/i386/kernel/fi/pf/fi_pf.h Thu Jan 1 08:00:00 1970 +++ linux-2.5-kp-fi-dm-intcpts/linux-2.5.47/arch/i386/kernel/fi/pf/fi_pf.h Mon Nov 25 17:19:10 2002 @@ -0,0 +1,81 @@ +/************************************************************************** **** + * Fault Injection Test harness (FI) + * Copyright (C) Intel Crop. + * + * 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 2 + * 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + **************************************************************************** ** + */ + +#ifndef __PF_H_ +#define __PF_H_ + + +#define PF_MAJOR 1 +#define PF_MINOR 1 +#define PF_PATCH 0 + + +/* + * Delcare typedefs + */ +enum reason_type{ + NOT_ME = 0, /* page fault is not in regions */ + NOTHING = 1, /* access others point in regions */ + REG_READ = 2, /* read from addr to reg */ + REG_WRITE = 3, /* write from reg to addr */ + IMM_WRITE = 4, /* write from imm to addr */ + OTHERS = 5 /* Other instructions can not intercept */ +}; + +/* + * Declare functions in pf_interface.c + */ +void register_pf_interface(void); +void unregister_pf_interface(void); + + +/* + * Declare functions in pf_region.c + */ +void pf_lock_region_irqsave(unsigned long *flags); +void pf_lock_region(void); +void pf_unlock_region_irqrestore(unsigned long flags); +void pf_unlock_region(void); +int pf_find_region(unsigned long addr); +int pf_add_region(unsigned long addr); +void pf_release_region(int ri); +int pf_is_removed_regions(unsigned long addr); +void pf_clr_pte_bits(unsigned long addr, unsigned long bitmask); +void pf_set_pte_bits(unsigned long addr, unsigned long bitmask); +/* + * Declare functions in pf_in.c + */ +enum reason_type get_ins_type(unsigned long ins_addr); +unsigned int get_ins_width(unsigned long ins_addr); +unsigned long get_ins_reg_val(unsigned long ins_addr, struct pt_regs *regs); +void set_ins_reg_val(unsigned long addr, struct pt_regs *regs, unsigned long val); +unsigned long get_ins_imm_val(unsigned long ins_addr); +void set_ins_imm_val(unsigned long ins_addr, unsigned long val); + + +/* + * Declare functions in pf.c + */ +int pf_register(struct watchpoint wp, int id, struct iInterceptHook *hook, char *modname); +int pf_unregister(int id, char *modname); +int pf_corrupt(int id, __u32 dirty, void *data); + +#endif//__PF_H_ diff -Nur -X dontdiff linux-2.5-kp-fi-dm/linux-2.5.47/arch/i386/kernel/fi/pf/pf.c linux-2.5-kp-fi-dm-intcpts/linux-2.5.47/arch/i386/kernel/fi/pf/pf.c --- linux-2.5-kp-fi-dm/linux-2.5.47/arch/i386/kernel/fi/pf/pf.c Thu Jan 1 08:00:00 1970 +++ linux-2.5-kp-fi-dm-intcpts/linux-2.5.47/arch/i386/kernel/fi/pf/pf.c Mon Nov 25 17:19:41 2002 @@ -0,0 +1,449 @@ +/************************************************************************** **** + * Fault Injection Test harness (FI) + * Copyright (C) Intel Crop. + * + * 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 2 + * 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + **************************************************************************** ** + */ + +/* $Id: pf.c,v 1.2 2002/11/14 06:19:49 yzhuang Exp $ + * Copyright by Intel Crop., 2002 + * Louis Zhuang (lou...@in...) + */ + +/** + * @file pf.c + */ + +#include <linux/config.h> +#include <linux/version.h> + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/signal.h> +#include <linux/sched.h> +#include <linux/timer.h> +#include <linux/interrupt.h> +#include <linux/tty.h> +#include <linux/tty_flip.h> +#include <linux/major.h> +#include <linux/string.h> +#include <linux/fcntl.h> +#include <linux/ptrace.h> +#include <linux/ioport.h> +#include <linux/mm.h> +#include <linux/slab.h> +#include <asm/pgalloc.h> +#include <asm/io.h> +#include <asm/tlbflush.h> + +#include <asm/fi.h> + +#define PREFIX_NAME "FI_PF" +#include <linux/fi/fi.h> +#include <linux/fi/fi_interface.h> +#include <linux/fi/fi_internal.h> + +#include "fi_pf.h" + +long globalcounter=0; + +#define MAX_WP 32 + +/* Store all wps */ +static struct{ + int page_index; + struct watchpoint wp; + int id; + struct iInterceptHook * hook; +}wp_tb[MAX_WP] = { [0 ... MAX_WP-1] = { + wp: {0,0}, + id: -1, + hook: NULL } +}; + +static void fi_do_flush_tlb_ipi(void* addr) { + __flush_tlb_one(addr); +}; +static void fi_flush_tlb_all(unsigned long addr) { + if ((smp_call_function(fi_do_flush_tlb_ipi,(void*)addr,1,1))!=0) { + PERROR("can not flush tlb\n"); + } + __flush_tlb_one(addr); +}; + +int pf_register(struct watchpoint wp, int id, struct iInterceptHook *hook, char *modname){ + unsigned long flags; + int wi=0; //watchpoint index + int ri=0; //region index + int rv=0; + + /* Make sure that wp is vaild */ + if ( hook==NULL ) { + PERROR("Reason: %s\n", "/* There are no callback */"); + return -EFI_INVALID; + } + if ( wp.type& (~FI_WPTYPE_ALL_MASK) ) { + PERROR("Reason: %s\n", "/* There are reserved bits in use*/"); + return -EFI_INVALID; + } + if ( is_IO(wp.type) && (unsigned int)wp.addr>0xffff ) { + PERROR("Reason: %s\n", "/* beyond 64k IO space*/"); + return -EFI_INVALID; + } + + pf_lock_region_irqsave(&flags);//lock regions + + /* Make sure there are no the same wp or id */ + for (wi=0; wi<MAX_WP; wi++) { + if (wp_tb[wi].hook==NULL) continue; + if ((id==wp_tb[wi].id) || (wp.addr==wp_tb[wi].wp.addr)) { + PERROR("Reason: %s\n", "/* there are same wp or id */"); + rv = -EFI_REGISTERED; + goto err1; + } + } + + ri = pf_add_region((unsigned long)wp.addr); + if (ri<0) { + PERROR("can not add this region, %p\n", wp.addr); + goto err; + } + + /* find empty item in wp_tb */ + for (wi=0; wi<MAX_WP; wi++){ + if (wp_tb[wi].hook==NULL){ + break; + } + } + if (wi>=MAX_WP) { + rv = -EFI_TABFULL; + goto err1; + } + + /* register this wp into wp_tb*/ + wp_tb[wi].wp.addr = wp.addr; + wp_tb[wi].wp.type = wp.type; + wp_tb[wi].id = id; + wp_tb[wi].hook = hook; + + if(modname != NULL) + fi_lock_module(modname); + + PDEBUG("register WP: index=%d, addr=0X%p, type=0x%x, id=%d, hook=0x%p\n", + wi, wp_tb[wi].wp.addr, wp_tb[wi].wp.type, + wp_tb[wi].id, wp_tb[wi].hook); + err1: + err: + pf_unlock_region_irqrestore(flags); + fi_flush_tlb_all((unsigned long)wp.addr); + return rv; +}; + + +int pf_unregister(int id, char *modname) { + unsigned long flags; + int wi=0; + int ri=0; + int rv=0; + + pf_lock_region_irqsave(&flags); + + /* find the id */ + for (wi=0; wi<MAX_WP; wi++) { + if (wp_tb[wi].hook!=NULL && wp_tb[wi].id == id) { + break; + } + } + if (wi>=MAX_WP) { + rv = -EFI_NOTFOUND; + goto err; + } + + ri = pf_find_region((unsigned long)wp_tb[wi].wp.addr); + if (ri>=0) { + pf_release_region(ri); + }else{ + PERROR("can not release region #%d\n", ri); + } + + wp_tb[wi].wp.addr = 0; + wp_tb[wi].wp.type = 0; + wp_tb[wi].id = -1; + wp_tb[wi].hook = 0; + + if (modname != NULL) + fi_unlock_module(modname); + err: + pf_unlock_region_irqrestore(flags); + return rv; +}; + + +/** + * This is a KEY data structure. When interceptor triggers DM + * and then DM call iAction method. iAction remembers this corrupted data. + * Then DM returns to Interceptor and Interceptor returns corrupted data + * to intercepted access. + */ +struct fi_corrupt{ + __u32 corrupted_data; +}; + +int pf_corrupt(int id, __u32 dirty, void *data){ + ((struct fi_corrupt *)data)->corrupted_data = dirty; + return 0; +}; + + +inline static int find_wp(unsigned long addr){ + int i=0; + + for (i=0; i<MAX_WP; i++) { + if (wp_tb[i].hook == NULL) continue; + if (addr == (unsigned long)wp_tb[i].wp.addr) { + break; + } + } + return i; +}; + + +/* + * Handle page fault exception and debug exception + */ +struct { + unsigned long addr; + unsigned long eip; + enum reason_type type; + unsigned long val; + int index; +}pf_reason[NR_CPUS] = { [0 ... NR_CPUS-1] = {0, 0, NOT_ME, 0, 0} }; + +static unsigned long crpt_mask[] = +{ 0x000000FF, 0x0000FFFF, 0x00FFFFFF, 0xFFFFFFFF}; + +int pf_do_page_fault(struct pt_regs * regs, unsigned long address) { + unsigned long cpu = smp_processor_id(); + struct fi_corrupt crpt; + int i; + int ins_len, crpt_len; + + pf_lock_region(); //lock it! + if (pf_find_region(address)<0) { + if (pf_is_removed_regions(address)) { + pf_unlock_region(); + return 1; + } else { + goto not_me; + } + } + + pf_reason[cpu].type = NOTHING; + pf_reason[cpu].addr = address; + pf_reason[cpu].eip = regs->eip; + + i = find_wp(address); + if (i>=MAX_WP) { + PDEBUG("address=%#08lx is not watched, cpuid=%ld, type=%d\n", + address, cpu, pf_reason[cpu].type); + goto out; + } + + /* We find a wp for this pagefault */ + pf_reason[cpu].index= i; + pf_reason[cpu].type = get_ins_type(pf_reason[cpu].eip); + + PDEBUG("address=%#08lx is watched, cpuid=%ld, type=%d\n", + address, cpu, pf_reason[cpu].type); + + switch (pf_reason[cpu].type) { + case OTHERS: + PDEBUG("Hmm? Can not analyze the instruction, *eip=0x%08lx\n", + *(unsigned long *)pf_reason[cpu].eip); + break; + case REG_WRITE: + PDEBUG("reg_write instruction eip=0x%08lx\n", + pf_reason[cpu].eip); + if (is_write(wp_tb[i].wp.type)) { + + ins_len = get_ins_width(pf_reason[cpu].eip); + pf_reason[cpu].val = get_ins_reg_val(pf_reason[cpu]. eip, regs); + + crpt_len = (WP_LEN(wp_tb[i].wp)<ins_len)? + WP_LEN(wp_tb[i].wp):ins_len; + crpt.corrupted_data = + pf_reason[cpu].val & crpt_mask[crpt_len-1]; + ( *(wp_tb[i].hook->trigger) )( wp_tb[i].id, crpt.corrupted_data, + crpt_len, 0, &crpt); + + set_ins_reg_val(pf_reason[cpu].eip, regs, + (crpt.corrupted_data&crpt_mask[crpt_ len-1]) + | (pf_reason[cpu].val&(~crpt_mask[crpt_len-1]))); + } + break; + case IMM_WRITE: + PDEBUG("imm_write instruction eip=0x%08lx\n", + pf_reason[cpu].eip); + if (is_write(wp_tb[i].wp.type)) { + + ins_len = get_ins_width(pf_reason[cpu].eip); + pf_reason[cpu].val = get_ins_imm_val(pf_reason[cpu]. eip); + + crpt_len = (WP_LEN(wp_tb[i].wp)<ins_len)? + WP_LEN(wp_tb[i].wp):ins_len; + crpt.corrupted_data = pf_reason[cpu].val & crpt_mask[crpt_len-1]; + ( *(wp_tb[i].hook->trigger) )( wp_tb[i].id, crpt.corrupted_data, + crpt_len, 0, &crpt); + + set_ins_imm_val(pf_reason[cpu].eip, + (crpt.corrupted_data&crpt_mask[crpt_ len-1]) + | (pf_reason[cpu].val&(~crpt_mask[crpt_len-1]))); + } + break; + case REG_READ: + PDEBUG("reg_read instruction eip=0x%08lx\n", + pf_reason[cpu].eip); + /* we should trigger it in do_debug*/ + break; + default: + PERROR("error when getting instruction eip=0x%08lx\n", + pf_reason[cpu].eip); + break; + } + + out: + /* set TF */ + regs->eflags |= X86_EFLAGS_TF; + + /* clean page fault and tlb ONLY on this cpu */ + pf_set_pte_bits(pf_reason[cpu].addr, _PAGE_PRESENT); + + //__flush_tlb_one(pf_reason[cpu].addr); + + //NOTICE!!! we hold region lock and wp_tb lock now, release it in do_debug + return 1; + not_me: + pf_reason[cpu].type = NOT_ME; + pf_unlock_region(); + return 0; +}; + +int pf_do_debug(unsigned long condition, struct pt_regs * regs) { + unsigned long cpu = smp_processor_id(); + struct fi_corrupt crpt; + int ins_len, crpt_len; + + /* + * DProbes make do_debug disallow irq. So we are safty from irq now. + */ + + PDEBUG("cpuid=%ld, type=%d\n", cpu, pf_reason[cpu].type); + if (pf_reason[cpu].type == NOT_ME) goto not_me; + + switch (pf_reason[cpu].type) { + case OTHERS: + PDEBUG("can not analyze the instruction cond=0x%08lx\n", condition); + break; + case REG_WRITE: + PDEBUG( "restore regval after reg_write corrupting, val=0x%08lx\n", + pf_reason[cpu].val); + if (is_write(wp_tb[pf_reason[cpu].index].wp.type)) { + set_ins_reg_val(pf_reason[cpu].eip, regs, pf_reason[cpu].val); + } + break; + case IMM_WRITE: + PDEBUG("restore immval after imm_write corrputing, val=0x%08lx\n", + pf_reason[cpu].val); + if (is_write(wp_tb[pf_reason[cpu].index].wp.type)) { + set_ins_imm_val(pf_reason[cpu].eip, pf_reason[cpu].val); + } + break; + case REG_READ: + PDEBUG("corrupt reg after reg_read! cond=0x%08lx\n", condition); + + if (is_read(wp_tb[pf_reason[cpu].index].wp.type)) { + + ins_len = get_ins_width(pf_reason[cpu].eip); + pf_reason[cpu].val = get_ins_reg_val(pf_reason[cpu]. eip, regs); + + crpt_len = (WP_LEN(wp_tb[pf_reason[cpu].index].wp)<ins_len)? + WP_LEN(wp_tb[pf_reason[cpu].index].wp):ins_l en; + crpt.corrupted_data = pf_reason[cpu].val & crpt_mask[crpt_len-1]; + ( *(wp_tb[pf_reason[cpu].index].hook->trigger) )( + wp_tb[pf_reason[cpu].index].id, crpt.corrupted_data, + crpt_len, 0, &crpt); + + set_ins_reg_val(pf_reason[cpu].eip, regs, + (crpt.corrupted_data&crpt_mask[crpt_ len-1]) + | (pf_reason[cpu].val&(~crpt_mask[crpt_len-1]))); + } + break; + case NOTHING: + PDEBUG("just restore page fault! address=%#08lx\n", pf_reason[cpu].addr); + break; + case NOT_ME: + default: + PERROR("should never reach here! cond=0x%08lx\n", condition); + break; + } + pf_reason[cpu].type = NOT_ME; + + exit: + /* clear TF */ + regs->eflags &= ~X86_EFLAGS_TF; + /* clean page fault and tlb ONLY on this cpu */ + pf_clr_pte_bits(pf_reason[cpu].addr, _PAGE_PRESENT); + + __flush_tlb_one(pf_reason[cpu].addr); + + //Release lock which is locked in do_page_fault + pf_unlock_region(); + return 1; + + not_me: + return 0; +}; + + +/* + * Initialize/Uninitialize modules + */ +static int __init fi_pf_init(void) { + int rv=0; + + register_pf_interface(); + fi_post_page_fault=pf_do_debug; + fi_page_fault=pf_do_page_fault; + + PINFO("FI Pagefault interceptor v%d.%d.%d loaded.\n", PF_MAJOR, PF_MINOR, PF_PATCH); + EXPORT_NO_SYMBOLS; + return 0; +}; + +static void __exit fi_pf_cleanup(void) { + fi_page_fault=NULL; + fi_post_page_fault=NULL; + unregister_pf_interface(); +}; + +module_init(fi_pf_init); +module_exit(fi_pf_cleanup); +MODULE_AUTHOR("Louis Zhuang"); +MODULE_DESCRIPTION("Pagefault interceptor component for FI"); +MODULE_LICENSE("GPL"); diff -Nur -X dontdiff linux-2.5-kp-fi-dm/linux-2.5.47/arch/i386/kernel/fi/pf/pf_in.c linux-2.5-kp-fi-dm-intcpts/linux-2.5.47/arch/i386/kernel/fi/pf/pf_in.c --- linux-2.5-kp-fi-dm/linux-2.5.47/arch/i386/kernel/fi/pf/pf_in.c Thu Jan 1 08:00:00 1970 +++ linux-2.5-kp-fi-dm-intcpts/linux-2.5.47/arch/i386/kernel/fi/pf/pf_in.c Mon Nov 25 17:20:03 2002 @@ -0,0 +1,443 @@ +/************************************************************************** **** + * Fault Injection Test harness (FI) + * Copyright (C) Intel Crop. + * + * 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 2 + * 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + **************************************************************************** ** + */ + +/* $Id: pf_in.c,v 1.1.1.1 2002/11/12 05:56:32 brlock Exp $ + * Copyright by Intel Crop., 2002 + * Louis Zhuang (lou...@in...) + */ + +/** + * @file pf_hooks.c + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/signal.h> +#include <linux/sched.h> +#include <linux/timer.h> +#include <linux/interrupt.h> +#include <linux/tty.h> +#include <linux/tty_flip.h> +#include <linux/major.h> +#include <linux/string.h> +#include <linux/fcntl.h> +#include <linux/ioport.h> +#include <linux/mm.h> +#include <linux/slab.h> +#include <asm/io.h> + +#define PREFIX_NAME "FI_PF" +#include <linux/fi/fi.h> +#include <linux/fi/fi_interface.h> +#include <linux/fi/fi_internal.h> + +#include "fi_pf.h" + +#define NR_ELEMENTS(array) (sizeof(array)/sizeof(array[0])) + +/* IA32 Manual 3, 2-1 */ +static unsigned char prefix_codes[] = { + 0xF0, 0xF2, 0xF3, 0x2E, 0x36, 0x3E, 0x26, 0x64, + 0x65, 0x2E, 0x3E, 0x66, 0x67 +}; +int skip_prefix(unsigned char *addr, int *shorted) { + int i=0; + unsigned char *p=addr; + *shorted=0; + + restart: + for (i=0; i<NR_ELEMENTS(prefix_codes); i++) { + if (*p == prefix_codes[i]){ + if (*p == 0x66) *shorted = 1; + p++; + goto restart; + } + } + return p-addr; +}; + +int get_opcode(unsigned char *addr, unsigned int *opcode) { + int rv; + if (*addr == 0x0F){/*0x0F is extension instruction*/ + *opcode = *(unsigned short*)addr; + rv=2; + }else{ + *opcode = *addr; + rv=1; + } + return rv; +}; + +/* IA32 Manual 3, 3-432*/ +static unsigned int reg_rop[] = {0x8A, 0x8B, 0xB60F, 0xB70F, 0xBE0F, 0xBF0F}; +static unsigned int reg_wop[] = {0x88, 0x89}; +static unsigned int imm_wop[] = {0xC6, 0xC7}; + +enum reason_type get_ins_type(unsigned long ins_addr) { + unsigned int opcode; + unsigned char *p; + int shorted=0; + int i=0; + enum reason_type rv=OTHERS; + + p = (unsigned char *)ins_addr; + p += skip_prefix(p, &shorted); + p += get_opcode(p, &opcode); + + for (i=0; i<NR_ELEMENTS(reg_rop); i++) { + if (reg_rop[i]==opcode) { + rv = REG_READ; + goto exit; + } + } + + for (i=0; i<NR_ELEMENTS(reg_wop); i++) { + if (reg_wop[i]==opcode) { + rv = REG_WRITE; + goto exit; + } + } + + for (i=0; i<NR_ELEMENTS(imm_wop); i++) { + if (imm_wop[i]==opcode) { + rv = IMM_WRITE; + goto exit; + } + } + + exit: + return rv; +}; + +/* IA32 Manual 3, 3-432*/ +static unsigned int w8[] = {0x88, 0x8A, 0xC6}; +static unsigned int w32[]= {0x89, 0x8B, 0xC7, 0xB60F, 0xB70F, 0xBE0F, 0xBF0F}; +unsigned int get_ins_width(unsigned long ins_addr) { + unsigned int opcode; + unsigned char *p; + int shorted=0; + int i=0; + + p = (unsigned char *)ins_addr; + p += skip_prefix(p, &shorted); + p += get_opcode(p, &opcode); + + for (i=0; i<NR_ELEMENTS(w8); i++) { + if (w8[i]==opcode) { + return 1; + } + } + for (i=0; i<NR_ELEMENTS(w32); i++) { + if (w32[i]==opcode) { + if (shorted) { + return 2; + } else { + return 4; + } + } + } + + PERROR("Unknow opcode=0x%02x\n", opcode); + return 0; +}; +/* define register ident in mod/rm byte after undefine it in ptrace*/ +#undef EAX +#define EAX 0 +#define ECX 1 +#define EDX 2 +#undef EBX +#define EBX 3 +#define ESP 4 +#define EBP 5 +#undef ESI +#define ESI 6 +#undef EDI +#define EDI 7 + +#define AL 0 +#define CL 1 +#define DL 2 +#define BL 3 +#define AH 4 +#define CH 5 +#define DH 6 +#define BH 7 +unsigned char *get_reg_w8(int no, struct pt_regs *regs){ + unsigned char *rv=NULL; + + switch (no) { + case AL: + rv = (unsigned char *)®s->eax; + break; + case BL: + rv = (unsigned char *)®s->ebx; + break; + case CL: + rv = (unsigned char *)®s->ecx; + break; + case DL: + rv = (unsigned char *)®s->edx; + break; + case AH: + rv = 1+(unsigned char *)®s->eax; + break; + case BH: + rv = 1+(unsigned char *)®s->ebx; + break; + case CH: + rv = 1+(unsigned char *)®s->ecx; + break; + case DH: + rv = 1+(unsigned char *)®s->edx; + break; + default: + PERROR("Error reg no# %d\n", no); + break; + } + return rv; +}; + +unsigned long *get_reg_w32(int no, struct pt_regs *regs){ + unsigned long *rv=NULL; + + switch (no) { + case EAX: + rv = ®s->eax; + break; + case EBX: + rv = ®s->ebx; + break; + case ECX: + rv = ®s->ecx; + break; + case EDX: + rv = ®s->edx; + break; + case ESP: + rv = ®s->esp; + break; + case EBP: + rv = ®s->ebp; + break; + case ESI: + rv = ®s->esi; + break; + case EDI: + rv = ®s->edi; + break; + default: + PERROR("Error reg no# %d\n", no); + } + return rv; +}; + +unsigned long get_ins_reg_val(unsigned long ins_addr, struct pt_regs *regs) { + unsigned int opcode; + unsigned char mod_rm; + int reg; + unsigned char *p; + int shorted=0; + int i=0; + unsigned long rv; + + p = (unsigned char *)ins_addr; + p += skip_prefix(p, &shorted); + p += get_opcode(p, &opcode); + for (i=0; i<NR_ELEMENTS(reg_rop); i++) { + if (reg_rop[i]==opcode) { + rv = REG_READ; + goto do_work; + } + } + + for (i=0; i<NR_ELEMENTS(reg_wop); i++) { + if (reg_wop[i]==opcode) { + rv = REG_WRITE; + goto do_work; + } + } + PERROR("Not a register instruction, opcode=0x%02x\n", opcode); + goto err; + do_work: + mod_rm = *p; + reg = (mod_rm>>3)&0x7; + if (get_ins_width(ins_addr)==1) { + return *get_reg_w8(reg, regs); + }else if (get_ins_width(ins_addr)==2) { + return *(unsigned short*)get_reg_w32(reg, regs); + }else if (get_ins_width(ins_addr)==4) { + return *get_reg_w32(reg, regs); + }else{ + PERROR("Error width# %d\n", reg); + goto err; + } + err: + return 0; +}; + + +void set_ins_reg_val(unsigned long ins_addr, struct pt_regs *regs, unsigned long val) { + unsigned int opcode; + unsigned char mod_rm; + int reg; + unsigned char *p; + int shorted=0; + int i=0; + unsigned long rv; + + p= (unsigned char *)ins_addr; + p += skip_prefix(p, &shorted); + p += get_opcode(p, &opcode); + for (i=0; i<NR_ELEMENTS(reg_rop); i++) { + if (reg_rop[i]==opcode) { + rv = REG_READ; + goto do_work; + } + } + + for (i=0; i<NR_ELEMENTS(reg_wop); i++) { + if (reg_wop[i]==opcode) { + rv = REG_WRITE; + goto do_work; + } + } + PERROR("Not a register instruction, opcode=0x%02x\n", opcode); + goto err; + do_work: + mod_rm = *p; + reg = (mod_rm>>3)&0x7; + if (get_ins_width(ins_addr)==1) { + *get_reg_w8(reg, regs) = val; + }else if (get_ins_width(ins_addr)==2) { + *(unsigned short*)get_reg_w32(reg, regs) = val; + }else if (get_ins_width(ins_addr)==4) { + *get_reg_w32(reg, regs) = val; + }else{ + PERROR("Error width, reg=%d\n", reg); + goto err; + } + err: + return; + +}; + +unsigned long get_ins_imm_val(unsigned long ins_addr) { + unsigned int opcode; + unsigned char mod_rm; + unsigned char mod; + unsigned char *p; + int shorted=0; + int i=0; + unsigned long rv; + + p= (unsigned char *)ins_addr; + p += skip_prefix(p, &shorted); + p += get_opcode(p, &opcode); + for (i=0; i<NR_ELEMENTS(imm_wop); i++) { + if (imm_wop[i]==opcode) { + rv = IMM_WRITE; + goto do_work; + } + } + PERROR("Not a imm instruction, opcode=0x%02x\n", opcode); + goto err; + do_work: + mod_rm = *p; + mod = mod_rm>>6; + p++; + switch (mod) { + case 0: + break; + case 1: + p += 1; + break; + case 2: + p += 4; + case 3: + default: + PERROR("it is not a memory access instruction, rm_mod=0x%02x\n", mod_rm); + } + if (get_ins_width(ins_addr)==1) { + return *(unsigned char *)p; + }else if (get_ins_width(ins_addr)==2) { + return *(unsigned short*)p; + }else if (get_ins_width(ins_addr)==4) { + return *(unsigned long*)p; + }else{ + PERROR("Error width%s\n", "."); + goto err; + } + err: + return 0; +}; + +void set_ins_imm_val(unsigned long ins_addr, unsigned long val) { + unsigned int opcode; + unsigned char mod_rm; + unsigned char mod; + unsigned char *p; + int shorted=0; + int i=0; + unsigned long rv; + + p= (unsigned char *)ins_addr; + p += skip_prefix(p, &shorted); + p += get_opcode(p, &opcode); + for (i=0; i<NR_ELEMENTS(imm_wop); i++) { + if (imm_wop[i]==opcode) { + rv = IMM_WRITE; + goto do_work; + } + } + PERROR("Not a imm instruction, opcode=0x%02x\n", opcode); + goto err; + do_work: + mod_rm = *p; + mod = mod_rm>>6; + p++; + switch (mod) { + case 0: + break; + case 1: + p += 1; + break; + case 2: + p += 4; + break; + case 3: + default: + PERROR("it is not a memory access instruction, rm_mod=0x%02x\n", mod_rm); + } + if (get_ins_width(ins_addr)==1) { + *(unsigned char *)p = val; + }else if (get_ins_width(ins_addr)==2) { + *(unsigned short*)p = val; + }else if (get_ins_width(ins_addr)==4) { + *(unsigned long*)p = val; + }else{ + PERROR("Error width%s\n", "."); + goto err; + } + err: + return; +}; diff -Nur -X dontdiff linux-2.5-kp-fi-dm/linux-2.5.47/arch/i386/kernel/fi/pf/pf_interface.c linux-2.5-kp-fi-dm-intcpts/linux-2.5.47/arch/i386/kernel/fi/pf/pf_interface. c --- linux-2.5-kp-fi-dm/linux-2.5.47/arch/i386/kernel/fi/pf/pf_interface.c Thu Jan 1 08:00:00 1970 +++ linux-2.5-kp-fi-dm-intcpts/linux-2.5.47/arch/i386/kernel/fi/pf/pf_interface. c Mon Nov 25 17:20:21 2002 @@ -0,0 +1,81 @@ +/************************************************************************** **** + * Fault Injection Test harness (FI) + * Copyright (C) Intel Crop. + * + * 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 2 + * 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + **************************************************************************** ** + */ + +/* $Id: pf_interface.c,v 1.1.1.1 2002/11/12 05:56:32 brlock Exp $ + * Copyright by Intel Crop., 2002 + * Louis Zhuang (lou...@in...) + */ + +/** + * @file pf_interface.c + */ + +#include <linux/config.h> +#include <linux/version.h> + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/signal.h> +#include <linux/sched.h> +#include <linux/timer.h> +#include <linux/interrupt.h> +#include <linux/tty.h> +#include <linux/tty_flip.h> +#include <linux/major.h> +#include <linux/string.h> +#include <linux/fcntl.h> +#include <linux/ptrace.h> +#include <linux/ioport.h> +#include <linux/mm.h> +#include <linux/slab.h> +#include <asm/io.h> + +#define PREFIX_NAME "FI_PF" +#include <linux/fi/fi.h> +#include <linux/fi/fi_interface.h> +#include <linux/fi/fi_internal.h> + +#include "fi_pf.h" + + +#define COMPONENT_NAME "fi.interceptor.pf" +#define INTERFACE_NAME COMPONENT_NAME ".iIntercept" + +static struct iIntercept intcpt[1] = { + { + wp_register: pf_register, + wp_unregister: pf_unregister, + corrupt: pf_corrupt, + } +}; + + +void register_pf_interface(void) { + inter_module_register(INTERFACE_NAME, THIS_MODULE, intcpt); + PDEBUG("register interface: %s\n", INTERFACE_NAME); +}; + +void unregister_pf_interface(void) { + inter_module_unregister(INTERFACE_NAME); + PDEBUG("unregister interface: %s\n", INTERFACE_NAME); +}; diff -Nur -X dontdiff linux-2.5-kp-fi-dm/linux-2.5.47/arch/i386/kernel/fi/pf/pf_region.c linux-2.5-kp-fi-dm-intcpts/linux-2.5.47/arch/i386/kernel/fi/pf/pf_region.c --- linux-2.5-kp-fi-dm/linux-2.5.47/arch/i386/kernel/fi/pf/pf_region.c Thu Jan 1 08:00:00 1970 +++ linux-2.5-kp-fi-dm-intcpts/linux-2.5.47/arch/i386/kernel/fi/pf/pf_region.c Mon Nov 25 17:20:31 2002 @@ -0,0 +1,212 @@ +/************************************************************************** **** + * Fault Injection Test harness (FI) + * Copyright (C) Intel Crop. + * + * 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 2 + * 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + **************************************************************************** ** + */ + +/* $Id: pf_region.c,v 1.2 2002/11/14 06:19:49 yzhuang Exp $ + * Copyright by Intel Crop., 2002 + * Louis Zhuang (lou...@in...) + */ + +/** + * @file pf_utils.c + */ + +#include <linux/config.h> +#include <linux/version.h> + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/signal.h> +#include <linux/sched.h> +#include <linux/timer.h> +#include <linux/interrupt.h> +#include <linux/tty.h> +#include <linux/tty_flip.h> +#include <linux/major.h> +#include <linux/string.h> +#include <linux/fcntl.h> +#include <linux/ptrace.h> +#include <linux/ioport.h> +#include <linux/mm.h> +#include <linux/slab.h> +#include <asm/delay.h> +#include <asm/io.h> +#include <asm/pgalloc.h> + +#define PREFIX_NAME "FI_PF" +#include <linux/fi/fi.h> +#include <linux/fi/fi_interface.h> +#include <linux/fi/fi_internal.h> + +#include "fi_pf.h" + +#define MAX_REGION 32 + +/* Store all pages which we intercept, other page info can be found + * in page directory entry */ +static struct{ + unsigned long addr; + int count;//count==0 is criterion of empty region +}region[MAX_REGION] = { [0 ... MAX_REGION-1] = {0, 0} }; +static spinlock_t region_lock = SPIN_LOCK_UNLOCKED; + + +static unsigned long removed_region[MAX_REGION] = { + [0 ... MAX_REGION-1] = 0 +}; + + +static int is_in_region(int i, unsigned long addr) { + if ( region[i].count!=0 + && (addr&PAGE_MASK)==(region[i].addr&PAGE_MASK)) { + return 1; + }else{ + return 0; + } +}; + +static pte_t *get_pte(unsigned long address) { + pgd_t *pgd = pgd_offset_k(address); + pmd_t *pmd = pmd_offset(pgd, address); + if (pmd_large(*pmd)) + return (pte_t *)pmd; + return pte_offset_kernel(pmd, address); +}; + +/** + * Set/Clear pte bits + */ +void pf_clr_pte_bits(unsigned long addr, unsigned long bitmask) { + pte_t *pte; + pte = get_pte(addr); + set_pte( pte, __pte( pte_val(*pte) & ~bitmask) ); +}; + +void pf_set_pte_bits(unsigned long addr, unsigned long bitmask) { + pte_t *pte; + pte = get_pte(addr); + set_pte( pte, __pte( pte_val(*pte) | bitmask) ); +}; + +/** + * lock_region_irqsave lock the region (that means lock all regions access). + * @param flags the saved flags. + */ +void pf_lock_region_irqsave(unsigned long *flags) { + spin_lock_irqsave(®ion_lock, *flags); +}; +void pf_lock_region(void) { + spin_lock(®ion_lock); +}; + +/** + * unlock region which is locked by lock_region_irqsave. + * @param flags restoring flags. + */ +void pf_unlock_region_irqrestore(unsigned long flags) { + spin_unlock_irqrestore(®ion_lock, flags); +}; +void pf_unlock_region(void) { + spin_unlock(®ion_lock); +}; + +/* find region in region table + * @return < 0 error, no region is found, + * >=0 the region index. + * @param addr the address which is in region. + */ +int pf_find_region(unsigned long addr) { + int i=0; + + for (i=0; i<MAX_REGION; i++) { + if (is_in_region(i, addr)) { + break; + } + } + + if (i<MAX_REGION) { + return i; + }else{ + return -1;//region is not locked! + } +}; + +/** + * increase region count and add region for the addr + * if the region is not here. + * @return <0 can not add the region + * >0 region index + * @param addr adding address + */ +int pf_add_region(unsigned long addr) { + int i=0; + + for (i=0; i<MAX_REGION; i++) { + if (is_in_region(i, addr)) { + region[i].count++; + goto out; + } + } + + //if there is not this region. create one. + for (i=0; i<MAX_REGION; i++) { + if (region[i].count==0) { + region[i].count= 1; + region[i].addr = addr&PAGE_MASK; + pf_clr_pte_bits(region[i].addr, _PAGE_PRESENT); +// flush_tlb_all(); + goto out; + } + } + err: + return -1; + out: + return i; +}; + +/** + * release region. when count meet zero, set present in page table. + * move this into garbage. + */ +void pf_release_region(int ri) { + static int removed_pos=0; + + region[ri].count--; + if (region[ri].count==0) { + pf_set_pte_bits(region[ri].addr, _PAGE_PRESENT); + removed_region[removed_pos] = region[ri].addr; + removed_pos++; + removed_pos %= MAX_REGION; + }else if (region[ri].count<0) { + PERROR("remove region, count=%d\n", region[ri].count); + } +}; + +int pf_is_removed_regions(unsigned long addr) { + int i=0; + for (i=0; i<MAX_REGION; i++) { + if (removed_region[i]==(addr&PAGE_MASK)) { + return 1; + } + } + return 0; +}; |