From: <he...@us...> - 2005-01-07 23:40:24
|
Update of /cvsroot/gc-linux/linux/drivers/misc In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv15050 Modified Files: Kconfig Makefile Added Files: gcn-mi.c gcn-mi.h Log Message: Add Nintendo GameCube Memory Interface driver. Useful to detect weird hardware behaviour and to hard protect some special memory areas, like the kexec preservation area. Change GCN_GQR to GAMECUBE_GQR, like in the rest of Kconfig options. --- NEW FILE: gcn-mi.c --- /* * arch/ppc/platforms/gcn-mi.c * * Nintendo GameCube Memory Interface driver * Copyright (C) 2004-2005 The GameCube Linux Team * Copyright (C) 2004,2005 Albert Herranz * * 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. * */ #include <linux/kernel.h> #include <linux/module.h> #include <linux/init.h> #include <linux/device.h> #include <linux/ioport.h> #include <linux/interrupt.h> #include <linux/spinlock.h> #include <linux/proc_fs.h> #include <asm/io.h> #include "gcn-mi.h" #include "../../arch/ppc/platforms/gamecube.h" #define MI_IRQ 7 #define MI_BASE 0xcc004000 #define MI_SIZE 0x80 #define MI_PROT_REGION0 ((u32 __iomem *)(MI_BASE+0x00)) #define MI_PROT_REGION1 ((u32 __iomem *)(MI_BASE+0x04)) #define MI_PROT_REGION2 ((u32 __iomem *)(MI_BASE+0x08)) #define MI_PROT_REGION3 ((u32 __iomem *)(MI_BASE+0x0c)) #define MI_PROT_TYPE ((u16 __iomem *)(MI_BASE+0x10)) #define MI_IMR ((u16 __iomem *)(MI_BASE+0x1c)) #define MI_ICR ((u16 __iomem *)(MI_BASE+0x1e)) #define MI_0x4020 ((u16 __iomem *)(MI_BASE+0x20)) #define MI_ADDRLO ((u16 __iomem *)(MI_BASE+0x22)) #define MI_ADDRHI ((u16 __iomem *)(MI_BASE+0x24)) #define MI_PAGE_SHIFT 10 #define MI_PAGE_MASK (~((1 << MI_PAGE_SHIFT) - 1)) #define MI_PAGE_SIZE (1UL << MI_PAGE_SHIFT) struct mi_private { struct device *device; int irq; int nr_regions; int regions_bitmap; unsigned long faults[MI_MAX_REGIONS+1]; unsigned long last_address; unsigned long last_address_faults; spinlock_t lock; #ifdef CONFIG_PROC_FS struct proc_dir_entry *proc_file; #endif }; static struct mi_private *mi_private; #define DRV_MODULE_NAME "gcn-mi" #define DRV_DESCRIPTION "Nintendo GameCube Memory Interface driver" #define DRV_AUTHOR "Albert Herranz" #define PFX DRV_MODULE_NAME ": " #define mi_printk(level, format, arg...) \ printk(level PFX format , ## arg) /** * */ static int mi_setup_default_regions(void) { int retval = 0; #ifndef MODULE #ifdef CONFIG_KEXEC retval = gcn_mi_region_protect(GCN_PRESERVE_TO, GCN_PRESERVE_TO + PAGE_ALIGN(GCN_PRESERVE_SIZE), MI_PROT_RO); if (retval < 0) { mi_printk(KERN_ERR, "unable to protect" " kexec reserved memory\n"); } #endif /* CONFIG_KEXEC */ #endif /* MODULE */ return retval; } /** * */ static irqreturn_t mi_handler(int this_irq, void *data, struct pt_regs *regs) { struct mi_private *priv = (struct mi_private *)data; unsigned long flags; int region, cause, ack; unsigned long address; spin_lock_irqsave(priv->lock, flags); address = readw(MI_ADDRLO) | (readw(MI_ADDRHI)<<16); ack = 0; cause = readw(MI_ICR); /* a fault was detected in some of the registered regions */ if ( (cause & 0xf) != 0) { for (region = 0; region < MI_MAX_REGIONS; region++) { if ( (cause & (1 << region)) != 0 ) { priv->faults[region]++; mi_printk(KERN_INFO, "bad access on region #%d" " at 0x%lx\n", region, address); } } } /* a fault was detected out of any registered region */ if ( (cause & (1 << 4)) != 0 ) { priv->faults[MI_MAX_REGIONS]++; if (address == priv->last_address) { priv->last_address_faults++; } else { #if 0 if (priv->last_address_faults > 0) { mi_printk(KERN_INFO, "bad access" " at 0x%lx (%lu times)\n", priv->last_address, priv->last_address_faults); } #endif priv->last_address = address; priv->last_address_faults = 1; } } ack |= cause; writew(ack, MI_ICR); /* ack int */ writew(0, MI_0x4020); /* kind of ack */ spin_unlock_irqrestore(priv->lock, flags); return IRQ_HANDLED; } #ifdef CONFIG_PROC_FS /** * */ static int mi_proc_read(char *page, char **start, off_t off, int count, int *eof, void *data) { struct mi_private *priv = (struct mi_private *)data; int len; int region; len = sprintf(page, "# <region> <faults>\n"); for (region = 0; region < MI_MAX_REGIONS; region++) { if ( (priv->regions_bitmap & (1<<region)) != 0 ) { len += sprintf(page+len, "%d\t%lu\n", region, priv->faults[region]); } } len += sprintf(page+len, "%s\t%lu\n", "none", priv->faults[MI_MAX_REGIONS]); return len; } #endif /* CONFIG_PROC_FS */ /** * */ static int mi_setup_irq(struct mi_private *priv) { int retval; retval = request_irq(priv->irq, mi_handler, 0, DRV_MODULE_NAME, priv); if (retval) { mi_printk(KERN_ERR, "request of irq%d failed\n", priv->irq); } else { writew((1<<4), MI_IMR); /* do not mask all MI interrupts */ } return retval; } /** * */ static int mi_probe(struct device *device, struct resource *mem, int irq) { struct mi_private *priv; int retval; priv = kmalloc(sizeof(struct mi_private), GFP_KERNEL); if (!priv) { retval = -ENOMEM; goto err; } memset(priv, 0, sizeof(*priv)); /* int region; priv->nr_regions = 0; priv->regions_bitmap = 0; for( region = 0; region < MI_MAX_REGIONS; region++ ) { priv->faults[region] = 0; } priv->last_address_faults = 0; */ priv->device = device; dev_set_drvdata(priv->device, priv); priv->irq = irq; retval = mi_setup_irq(priv); if (retval) goto err_setup_irq; #ifdef CONFIG_PROC_FS struct platform_device *pdev = to_platform_device(device); priv->proc_file = create_proc_read_entry(pdev->dev.bus_id, 0444, NULL, mi_proc_read, priv); priv->proc_file->owner = THIS_MODULE; #endif /* CONFIG_PROC_FS */ mi_private = priv; mi_setup_default_regions(); return 0; err_setup_irq: dev_set_drvdata(priv->device, NULL); kfree(priv); err: return retval; } /** * */ static void mi_shutdown(struct mi_private *priv) { gcn_mi_region_unprotect_all(); } /** * */ static void mi_remove(struct mi_private *priv) { #ifdef CONFIG_PROC_FS struct platform_device *pdev = to_platform_device(priv->device); remove_proc_entry(pdev->dev.bus_id, NULL); #endif /* CONFIG_PROC_FS */ mi_shutdown(priv); /* free interrupt handler */ free_irq(priv->irq, priv); kfree(priv); mi_private = NULL; } /** * */ static int __init mi_drv_probe(struct device *device) { struct platform_device *pdev = to_platform_device(device); struct resource *mem; int irq; irq = platform_get_irq(pdev, 0); mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!mem) return -ENODEV; mi_printk(KERN_INFO, "%s\n", DRV_DESCRIPTION); return mi_probe(device, mem, irq); } /** * */ static int mi_drv_remove(struct device *device) { struct mi_private *priv = dev_get_drvdata(device); if (priv) { mi_remove(priv); dev_set_drvdata(device, NULL); } return 0; } /** * */ static void mi_drv_shutdown(struct device *device) { struct mi_private *priv = dev_get_drvdata(device); if (priv) mi_shutdown(priv); } static struct device_driver mi_device_driver = { .name = "mi", .bus = &platform_bus_type, .probe = mi_drv_probe, .remove = mi_drv_remove, .shutdown = mi_drv_shutdown, }; static struct resource mi_resources[] = { [0] = { .start = MI_BASE, .end = MI_BASE + MI_SIZE -1, .flags = IORESOURCE_MEM, }, [1] = { .start = MI_IRQ, .end = MI_IRQ, .flags = IORESOURCE_IRQ, }, }; static struct platform_device mi_device = { .name = "mi", .id = 0, .num_resources = ARRAY_SIZE(mi_resources), .resource = mi_resources, }; /** * */ static int __init mi_init(void) { int retval = 0; retval = driver_register(&mi_device_driver); if (!retval) { retval = platform_device_register(&mi_device); } return retval; } /** * */ static void __exit mi_exit(void) { platform_device_unregister(&mi_device); driver_unregister(&mi_device_driver); } module_init(mi_init); module_exit(mi_exit); /* Exported symbols */ /** * */ int gcn_mi_region_protect(unsigned long physlo, unsigned long physhi, int type) { struct mi_private *priv = mi_private; int region, free_regions; u16 pagelo, pagehi; if (!priv) return -ENODEV; if (type < MI_PROT_NONE || type > MI_PROT_RW) return -EINVAL; if ( (physlo & ~MI_PAGE_MASK) != 0 || (physhi & ~MI_PAGE_MASK) != 0 ) { return -EINVAL; } free_regions = MI_MAX_REGIONS - priv->nr_regions; if (free_regions <= 0) { return -ENOMEM; } for (region = 0; region < MI_MAX_REGIONS; region++) { if ( (priv->regions_bitmap & (1<<region)) == 0 ) break; } if (region >= MI_MAX_REGIONS) return -ENOMEM; priv->regions_bitmap |= (1 << region); priv->nr_regions++; writew((readw(MI_PROT_TYPE) & ~(3 << 2*region))|(type << 2*region), MI_PROT_TYPE); pagelo = physlo >> MI_PAGE_SHIFT; pagehi = (physhi >> MI_PAGE_SHIFT) - 1; writel((pagelo << 16) | pagehi, MI_PROT_REGION0 + 4*region); writew(readw(MI_IMR) | (1 << region), MI_IMR); mi_printk(KERN_INFO, "protected region #%d" " from 0x%0lx to 0x%0lx with 0x%0x\n", region, (unsigned long)(pagelo << MI_PAGE_SHIFT), (unsigned long)(((pagehi+1) << MI_PAGE_SHIFT) - 1), type); return region; } /** * */ int gcn_mi_region_unprotect(int region) { struct mi_private *priv = mi_private; if (!priv) return -ENODEV; if (region < 0 || region > MI_MAX_REGIONS) return -EINVAL; writew(readw(MI_IMR) & ~(1 << region), MI_IMR); writel(0, MI_PROT_REGION0 + 4*region); writew(readw(MI_PROT_TYPE) | (MI_PROT_RW << 2*region), MI_PROT_TYPE); if ( (priv->regions_bitmap & (1<<region)) != 0 ) mi_printk(KERN_INFO, "region #%d unprotected\n", region); priv->regions_bitmap &= ~(1 << region); priv->nr_regions--; return 0; } /** * */ void gcn_mi_region_unprotect_all(void) { int region; writew(0, MI_IMR); for (region = 0; region < MI_MAX_REGIONS; region++) { gcn_mi_region_unprotect(region); } } MODULE_DESCRIPTION(DRV_DESCRIPTION); MODULE_AUTHOR(DRV_AUTHOR); MODULE_LICENSE("GPL"); --- NEW FILE: gcn-mi.h --- /* * arch/ppc/platforms/gcn-mi.h * * Nintendo GameCube Memory Interface driver * Copyright (C) 2004-2005 The GameCube Linux Team * Copyright (C) 2004,2005 Albert Herranz * * 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. * */ #ifndef __GCN_MI_H #define __GCN_MI_H #ifdef CONFIG_GAMECUBE_MI #define MI_MAX_REGIONS 4 #define MI_PROT_NONE 0x00 #define MI_PROT_RO 0x01 #define MI_PROT_WO 0x02 #define MI_PROT_RW 0x03 int gcn_mi_region_protect(unsigned long physlo, unsigned long physhi, int type); int gcn_mi_region_unprotect(int region); void gcn_mi_region_unprotect_all(void); #endif /* CONFIG_GAMECUBE_MI */ #endif /* __GCN_MI_H */ Index: Kconfig =================================================================== RCS file: /cvsroot/gc-linux/linux/drivers/misc/Kconfig,v retrieving revision 1.1 retrieving revision 1.2 diff -u -d -r1.1 -r1.2 --- Kconfig 2 Dec 2004 15:52:41 -0000 1.1 +++ Kconfig 7 Jan 2005 23:40:14 -0000 1.2 @@ -4,7 +4,7 @@ menu "Misc devices" -config GCN_GQR +config GAMECUBE_GQR tristate "Device driver for Gamecube Gekko GQR" depends on GAMECUBE default m @@ -14,6 +14,20 @@ psql and psqst instrutions. The registers will appear in /proc/sys/gqr. +config GAMECUBE_MI + tristate "Nintendo GameCube Memory Interface" + depends on GAMECUBE + default n + help + If you say yes to this option, support will be included for the + Memory Interface (MI) of the Nintendo GameCube. + + The MI allows one to setup up to four protected memory regions, + catching invalid accesses to them. The MI catches out of bounds + memory accesses too. + + If in doubt, say N here. + config IBM_ASM tristate "Device driver for IBM RSA service processor" depends on X86 && EXPERIMENTAL Index: Makefile =================================================================== RCS file: /cvsroot/gc-linux/linux/drivers/misc/Makefile,v retrieving revision 1.1 retrieving revision 1.2 diff -u -d -r1.1 -r1.2 --- Makefile 2 Dec 2004 15:55:53 -0000 1.1 +++ Makefile 7 Jan 2005 23:40:14 -0000 1.2 @@ -4,4 +4,5 @@ obj- := misc.o # Dummy rule to force built-in.o to be made obj-$(CONFIG_IBM_ASM) += ibmasm/ -obj-$(CONFIG_GCN_GQR) += gcn-gqr.o +obj-$(CONFIG_GAMECUBE_GQR) += gcn-gqr.o +obj-$(CONFIG_GAMECUBE_MI) += gcn-mi.o |