bitkeeper revision 1.2 (3ddb79c9KusG02eh7i-uXkgY0IksKA)

Import changeset
This commit is contained in:
smh22@boulderdash.cl.cam.ac.uk committed 2002-11-20 12:02:17 +00:00
1 parent 19e522da85
commit 4676bbf96d
401 files changed
+128584

No files matched your search

+400
View File
@@ -1,2 +1,402 @@
3ddb6b0bKlMz_dz-M59a1mkUa1lASw BitKeeper/etc/config
3ddb6b0buTaC5zg1_a8FoAR9FWi_mw BitKeeper/etc/ignore
3ddb79c9_hgSp-gsQm8HqWM_9W3B_A BitKeeper/etc/logging_ok
3ddb79bcbOVHh38VJzc97-JEGD4dJQ xen-2.4.16/Makefile
3ddb79bcCa2VbsMp7mWKlhgwLQUQGA xen-2.4.16/README
3ddb79bcWnTwYsQRWl_PaneJfa6p0w xen-2.4.16/Rules.mk
3ddb79bcZbRBzT3elFWSX7u6NtMagQ xen-2.4.16/arch/i386/Makefile
3ddb79bcBQF85CfLS4i1WGZ4oLLaCA xen-2.4.16/arch/i386/Rules.mk
3ddb79bcsjinG9k1KcvbVBuas1R2dA xen-2.4.16/arch/i386/apic.c
3ddb79bcSC_LvnmFlX-T5iTgaR0SKg xen-2.4.16/arch/i386/boot/boot.S
3ddb79bcUrk2EIaM5VsT6wUudH1kkg xen-2.4.16/arch/i386/delay.c
3ddb79bcecupHj56ZbTa3B0FxDowMg xen-2.4.16/arch/i386/entry.S
3ddb79bcY5zW7KhvI9gvfuPi3ZumEg xen-2.4.16/arch/i386/extable.c
3ddb79bcesE5E-lS4QhRhlqXxqj9cA xen-2.4.16/arch/i386/i387.c
3ddb79bcCAq6IpdkHueChoVTfXqEQQ xen-2.4.16/arch/i386/i8259.c
3ddb79bcBit4xJXbwtX0kb1hh2uO1Q xen-2.4.16/arch/i386/idle0_task.c
3ddb79bcKIkRR0kqWaJhe5VUDkMdxg xen-2.4.16/arch/i386/io_apic.c
3ddb79bc1uNlAtc-84Ioq4qfcnI_CQ xen-2.4.16/arch/i386/ioremap.c
3ddb79bdqfIcjkz_h9Hvtp8Tk_19Zw xen-2.4.16/arch/i386/irq.c
3ddb79bcHwuCQDjBICDTSis52hWguw xen-2.4.16/arch/i386/mm.c
3ddb79bdS4UeWWXDH-FaBKqcpMFcnw xen-2.4.16/arch/i386/mpparse.c
3ddb79bcnL-_Dtsbtjgxl7vJU3vBiQ xen-2.4.16/arch/i386/pci-dma.c
3ddb79bdeJ7_86z03yTAPIeeywOg3Q xen-2.4.16/arch/i386/pci-i386.c
3ddb79bdIKgipvGoqExEQ7jawfVowA xen-2.4.16/arch/i386/pci-i386.h
3ddb79bdHe6_Uij4-glW91vInNtBYQ xen-2.4.16/arch/i386/pci-irq.c
3ddb79bcZ_2FxINljqNSkqa17ISyJw xen-2.4.16/arch/i386/pci-pc.c
3ddb79bc1_2bAt67x9MFCP4AZrQnvQ xen-2.4.16/arch/i386/process.c
3ddb79bc7KxGCEJsgBnkDX7XjD_ZEQ xen-2.4.16/arch/i386/rwlock.c
3ddb79bcrD6Z_rUvSDgrvjyb4846Eg xen-2.4.16/arch/i386/setup.c
3ddb79bcSx2e8JSR3pdSGa8x1ScYzA xen-2.4.16/arch/i386/smp.c
3ddb79bcfUN3-UBCPzX26IU8bq-3aw xen-2.4.16/arch/i386/smpboot.c
3ddb79bc-Udq7ol-NX4q9XsYnN7A2Q xen-2.4.16/arch/i386/time.c
3ddb79bccYVzXZJyVaxuv5T42Z1Fsw xen-2.4.16/arch/i386/trampoline.S
3ddb79bcOftONV9h4QCxXOfiT0h91w xen-2.4.16/arch/i386/traps.c
3ddb79bc4nTpGQOe6_-MbyZzkhlhFQ xen-2.4.16/arch/i386/usercopy.c
3ddb79bcOMCu9-5mKpjIh5d0qqBDPg xen-2.4.16/arch/i386/xeno.lds
3ddb79bdff-gj-jFGKjOejeHLqL8Lg xen-2.4.16/common/Makefile
3ddb79bddEYJbcURvqqcx99Yl2iAhQ xen-2.4.16/common/block.c
3ddb79bdrqnW93GR9gZk1OJe1qK-iQ xen-2.4.16/common/brlock.c
3ddb79bdLX_P6iB7ILiblRLWvebapg xen-2.4.16/common/dom0_ops.c
3ddb79bdYO5D8Av12NHqPeSviav7cg xen-2.4.16/common/domain.c
3ddb79bdeyutmaXEfpQvvxj7eQ0fCw xen-2.4.16/common/event.c
3ddb79bd9drcFPVxd4w2GPOIjLlXpA xen-2.4.16/common/kernel.c
3ddb79bduhSEZI8xa7IbGQCpap5y2A xen-2.4.16/common/lib.c
3ddb79bdS39UXxUtZnaScie83-7VTQ xen-2.4.16/common/memory.c
3ddb79bdN51qpRC-6bOH-v5hl_AK6A xen-2.4.16/common/network.c
3ddb79bdD4SLmmdMD7yLW5HcUWucXw xen-2.4.16/common/page_alloc.c
3ddb79bdHqdQpATqC0rmUZNbsb6L6A xen-2.4.16/common/resource.c
3ddb79bdB9RNMnkQnUyZ5C9hhMSQQw xen-2.4.16/common/slab.c
3ddb79bd0gVQYmL2zvuJnldvD0AGxQ xen-2.4.16/common/softirq.c
3ddb79bdQqFHtHRGEO2dsxGgo6eAhw xen-2.4.16/common/timer.c
3ddb79bd3zgV33PHdt-cgh3sxcb1hw xen-2.4.16/common/vsprintf.c
3ddb79c0ppNeJtjC4va8j41ADCnchA xen-2.4.16/drivers/Makefile
3ddb79beWzgPS8ozf2BL2g3ZkiWhhQ xen-2.4.16/drivers/block/Makefile
3ddb79be04dyXzyXqDbMRS_1funwXQ xen-2.4.16/drivers/block/blkpg.c
3ddb79beME_0abStePF6fU8XLuQnWw xen-2.4.16/drivers/block/elevator.c
3ddb79beNQVrdGyoI4njXhgAjD6a4A xen-2.4.16/drivers/block/genhd.c
3ddb79beyWwLRP_BiM2t1JKgr_plEw xen-2.4.16/drivers/block/ll_rw_blk.c
3ddb79bdhcqD9ebrslr0O0oHqTiiXg xen-2.4.16/drivers/ide/Makefile
3ddb79bdErDn_WC3G-fWxKNR3viLnA xen-2.4.16/drivers/ide/ide-disk.c
3ddb79bdIPNW36FrlId94jTXaW8HoA xen-2.4.16/drivers/ide/ide-dma.c
3ddb79be5Ysvhn4se_Z-LQY_hI6UPw xen-2.4.16/drivers/ide/ide-features.c
3ddb79bdh1ohsWYRH_KdaXr7cqs12w xen-2.4.16/drivers/ide/ide-geometry.c
3ddb79bdYcxXT-2UEaDcG0Ic4MIK1g xen-2.4.16/drivers/ide/ide-pci.c
3ddb79bdOXTbcImJo8DwmlNX88k78Q xen-2.4.16/drivers/ide/ide-probe.c
3ddb79bdDWFwINnKn29RlFDwGJhjYg xen-2.4.16/drivers/ide/ide-taskfile.c
3ddb79bdkDY1bSOYkToP1Cc49VdBxg xen-2.4.16/drivers/ide/ide.c
3ddb79bdPyAvT_WZTAFhaX0jp-yXSw xen-2.4.16/drivers/ide/ide_modes.h
3ddb79bfogeJNHTIepPjd8fy1TyoTw xen-2.4.16/drivers/net/3c509.c
3ddb79bfMlOcWUwjtg6oMYhGySHDDw xen-2.4.16/drivers/net/3c59x.c
3ddb79bfl_DWxZQFKiJ2BXrSedV4lg xen-2.4.16/drivers/net/8139cp.c
3ddb79bfLVGtyXNJS4NQg-lP21rndA xen-2.4.16/drivers/net/8139too.c
3ddb79c0tWiE8xIFHszxipeVCGKTSA xen-2.4.16/drivers/net/Makefile
3ddb79bfU-H1Hms4BuJEPPydjXUEaQ xen-2.4.16/drivers/net/Space.c
3ddb79c0GejJrp1U6W4G6dYi-RiH4A xen-2.4.16/drivers/net/eepro100.c
3ddb79bfKvn9mt0kofpkw0QaWjxO6A xen-2.4.16/drivers/net/net_init.c
3ddb79c0fQgORkFlqWZdP-6cDHyFIQ xen-2.4.16/drivers/net/pcnet32.c
3ddb79bf_CBcu3QWYwq4bNAOnM2RqQ xen-2.4.16/drivers/net/setup.c
3ddb79bfh8ucmq_HqRSaURalpeAmPg xen-2.4.16/drivers/net/tulip/.depend
3ddb79bfsJ-hdQ17EXTFiUOHisjNgQ xen-2.4.16/drivers/net/tulip/21142.c
3ddb79bf0lzTL-ywAdOO7vctTYAmJA xen-2.4.16/drivers/net/tulip/ChangeLog
3ddb79bfRbGBTu5mznHtxpFPtnQYSQ xen-2.4.16/drivers/net/tulip/Makefile
3ddb79bfLLamkCaJZDJ7i6qrCUhwBw xen-2.4.16/drivers/net/tulip/eeprom.c
3ddb79bf-zt39-zIUgWC9Kb4As--Ew xen-2.4.16/drivers/net/tulip/interrupt.c
3ddb79bfdr1I4DtFnXCzpaHkEkHE2Q xen-2.4.16/drivers/net/tulip/media.c
3ddb79bftPnJDLCAo0Do4KPr5raERA xen-2.4.16/drivers/net/tulip/pnic.c
3ddb79bf5lr4NIjy1oZOM_jhpxGidw xen-2.4.16/drivers/net/tulip/pnic2.c
3ddb79bfBhawkz-2DT9lakMPbqzljQ xen-2.4.16/drivers/net/tulip/timer.c
3ddb79bf7yw7hGBMM60aMgMgxY_G2g xen-2.4.16/drivers/net/tulip/tulip.h
3ddb79bfhUr3baP8Lf4oZjhBX7i5kw xen-2.4.16/drivers/net/tulip/tulip_core.c
3ddb79beUWngyIhMHgyPtuTem4o4JA xen-2.4.16/drivers/pci/Makefile
3ddb79beU9td0Mnm0VUMklerBa37qQ xen-2.4.16/drivers/pci/compat.c
3ddb79beHkGQE58z5t5gyUCYiwOxvw xen-2.4.16/drivers/pci/gen-devlist.c
3ddb79bfoQcFKLf5P6wZlDl36alWdQ xen-2.4.16/drivers/pci/names.c
3ddb79bfyX7-pD6XdxY_mdNrJR20iw xen-2.4.16/drivers/pci/pci.c
3ddb79bf2AS7YBGwooE_Kbv7XgUqNQ xen-2.4.16/drivers/pci/pci.ids
3ddb79bfGf5-CZSdzn0DGBYWjQiDjw xen-2.4.16/drivers/pci/proc.c
3ddb79bf7sTn85WtP_8Nc2YEmmVExQ xen-2.4.16/drivers/pci/quirks.c
3ddb79bfkVLMq5CWjZLACPDivqxq_w xen-2.4.16/drivers/pci/setup-bus.c
3ddb79bfl1H1arbB0pzAEC2uPmY_3g xen-2.4.16/drivers/pci/setup-irq.c
3ddb79bfJaf0bkE1Y67bnll8-kjEPg xen-2.4.16/drivers/pci/setup-res.c
3ddb79bfIcCWJsBDNcQQE3ok2Azn-Q xen-2.4.16/drivers/pci/syscall.c
3ddb79be3kwzyKagpMHGoXZFdan7dg xen-2.4.16/drivers/scsi/Makefile
3ddb79beXZxwKh7cGyPfr40bhDyRrA xen-2.4.16/drivers/scsi/constants.h
3ddb79beGiGljlTNq_kRnCBZECgC9Q xen-2.4.16/drivers/scsi/hosts.h
3ddb79bexarQo1tQ541PPUyK9HXNDA xen-2.4.16/drivers/scsi/scsi.c
3ddb79beBOiYxQUiWTHosepRlJyuGA xen-2.4.16/drivers/scsi/scsi.h
3ddb79beVTYJj6_KMxYLJmCP7p9MuQ xen-2.4.16/drivers/scsi/scsi_dma.c
3ddb79beDrImFCFGgB_GLgUbeuHjog xen-2.4.16/drivers/scsi/scsi_error.c
3ddb79bepDvUltYDsInaUsH9lII9Sw xen-2.4.16/drivers/scsi/scsi_ioctl.c
3ddb79berPStE_-ILQHgcl1BLDLywA xen-2.4.16/drivers/scsi/scsi_lib.c
3ddb79beRXjB7_nNUbJMIRyjDmeByQ xen-2.4.16/drivers/scsi/scsi_merge.c
3ddb79beGNb7Es1bATZAGsPZEu5F2Q xen-2.4.16/drivers/scsi/scsi_module.c
3ddb79beZ--AZB0twliIm3qmQJO8Zg xen-2.4.16/drivers/scsi/scsi_obsolete.c
3ddb79beQgG_st0eBZUX8AQI7kBkHA xen-2.4.16/drivers/scsi/scsi_obsolete.h
3ddb79beK65cNRldY0CFGXjZ3-A74Q xen-2.4.16/drivers/scsi/scsi_proc.c
3ddb79beeIuwGDE0Ldl8wy6mt86Bag xen-2.4.16/drivers/scsi/scsi_queue.c
3ddb79beQVxjXLLSY896cqce3j6Ehg xen-2.4.16/drivers/scsi/scsi_scan.c
3ddb79beVrSvakLg_9MSo22vJ_TGrA xen-2.4.16/drivers/scsi/scsi_syms.c
3ddb79beC6PIqDEaxAfO3bLKcmMLeA xen-2.4.16/drivers/scsi/scsicam.c
3ddb79bedAG8DPsr3S1N4IASxUuBug xen-2.4.16/drivers/scsi/sd.c
3ddb79beA27dAK0xtNh4k6SJniKnlA xen-2.4.16/drivers/scsi/sd.h
3ddb79c3l4IiQtf6MS2jIzcd-hJS8g xen-2.4.16/include/asm-i386/apic.h
3ddb79c3QJYWr8LLGdonLbWmNb9pQQ xen-2.4.16/include/asm-i386/apicdef.h
3ddb79c3OiG9eTsi9Dy3F_OkuRAzKA xen-2.4.16/include/asm-i386/atomic.h
3ddb79c3rM-Ote0Xn6Ytg8Y6YqAG-A xen-2.4.16/include/asm-i386/bitops.h
3ddb79c3pXaTAGGWSIZF9EnRV5PlRw xen-2.4.16/include/asm-i386/byteorder.h
3ddb79c3KhTI0F_Iw_hRL9QEyOVK-g xen-2.4.16/include/asm-i386/cache.h
3ddb79c2LLt11EQHjrd6sB7FUqvFfA xen-2.4.16/include/asm-i386/cpufeature.h
3ddb79c2ADvRmdexd9y3AYK9_NTx-Q xen-2.4.16/include/asm-i386/current.h
3ddb79c2jFkPAZTDmU35L6IUssYMgQ xen-2.4.16/include/asm-i386/debugreg.h
3ddb79c3r9-31dIsewPV3P3i8HALsQ xen-2.4.16/include/asm-i386/delay.h
3ddb79c34BFiXjBJ_cCKB0aCsV1IDw xen-2.4.16/include/asm-i386/desc.h
3ddb79c2O729EttZTYu1c8LcsUO_GQ xen-2.4.16/include/asm-i386/elf.h
3ddb79c3NU8Zy40OTrq3D-i30Y3t4A xen-2.4.16/include/asm-i386/fixmap.h
3ddb79c39o75zPP0T1aQQ4mNrCAN2w xen-2.4.16/include/asm-i386/hardirq.h
3ddb79c3BFEIwXR4IsWbwp4BoL4DkA xen-2.4.16/include/asm-i386/hdreg.h
3ddb79c3TMDjkxVndKFKnGiwY0HzDg xen-2.4.16/include/asm-i386/i387.h
3ddb79c3otbjpnqFDSzSeD0J-0xcwg xen-2.4.16/include/asm-i386/ide.h
3ddb79c3fQ_O3o5NHK2N8AJdk0Ea4Q xen-2.4.16/include/asm-i386/io.h
3ddb79c2TKeScYHQZreTdHqYNLbehQ xen-2.4.16/include/asm-i386/io_apic.h
3ddb79c3S9Tga4XZRPrD4-aN3XIV6w xen-2.4.16/include/asm-i386/ioctl.h
3ddb79c2L7rTlFzazOLW1XuSZefpFw xen-2.4.16/include/asm-i386/irq.h
3ddb79c3I98vWcQR8xEo34JMJ4Ahyw xen-2.4.16/include/asm-i386/mc146818rtc.h
3ddb79c3n_UbPuxlkNxvvLycClIkxA xen-2.4.16/include/asm-i386/mpspec.h
3ddb79c2wa0dA_LGigxOelSGbJ284Q xen-2.4.16/include/asm-i386/msr.h
3ddb79c3xjYnrv5t3VqYlR4tNEOl4Q xen-2.4.16/include/asm-i386/page.h
3ddb79c3ysKUbxZuwKBRK3WXU2TlEg xen-2.4.16/include/asm-i386/pci.h
3ddb79c3nm2zdzeO6Mj8g7ex3txgGw xen-2.4.16/include/asm-i386/pgalloc.h
3ddb79c2QF5-pZGzuX4QukPCDAl59A xen-2.4.16/include/asm-i386/processor.h
3ddb79c3mbqEM7QQr3zVq7NiBNhouA xen-2.4.16/include/asm-i386/ptrace.h
3ddb79c2plf7ciNgoNjU-RsbUzawsw xen-2.4.16/include/asm-i386/rwlock.h
3ddb79c2mJI9YuGMScjofPlD8EdtgA xen-2.4.16/include/asm-i386/scatterlist.h
3ddb79c3Hgbb2g8CyWLMCK-6_ZVQSQ xen-2.4.16/include/asm-i386/smp.h
3ddb79c3jn8ALV_S9W5aeTYUQRKBpg xen-2.4.16/include/asm-i386/smpboot.h
3ddb79c3e9DCEoR-WzNxcOQDzLu7BQ xen-2.4.16/include/asm-i386/softirq.h
3ddb79c3NiyQE2vQnyGiaBnNjBO1rA xen-2.4.16/include/asm-i386/spinlock.h
3ddb79c3ezddh34MdelJpa5tNR00Dw xen-2.4.16/include/asm-i386/system.h
3ddb79c4HugMq7IYGxcQKFBpKwKhzA xen-2.4.16/include/asm-i386/types.h
3ddb79c3M2n1ROZH6xk3HbyN4CPDqg xen-2.4.16/include/asm-i386/uaccess.h
3ddb79c3uPGcP_l_2xyGgBSWd5aC-Q xen-2.4.16/include/asm-i386/unaligned.h
3ddb79c2YTaZwOqWin9-QNgHge5RVw xen-2.4.16/include/hypervisor-ifs/block.h
3ddb79c25UE59iu4JJcbRalx95mvcg xen-2.4.16/include/hypervisor-ifs/hypervisor-if.h
3ddb79c2oRPrzClk3zbTkRHlpumzKA xen-2.4.16/include/hypervisor-ifs/network.h
3ddb79c4qbCoOFHrv9sCGshbWzBVlQ xen-2.4.16/include/scsi/scsi.h
3ddb79c4R4iVwqIIeychVQYmIH4FUg xen-2.4.16/include/scsi/scsi_ioctl.h
3ddb79c4yw_mfd4Uikn3v_IOPRpa1Q xen-2.4.16/include/scsi/scsicam.h
3ddb79c4HKPMLvDBP9LxzPi_szVxGA xen-2.4.16/include/scsi/sg.h
3ddb79c0nTsjSpVK4ZVTI9WwN24xtQ xen-2.4.16/include/xeno/blk.h
3ddb79c0dVhTHLsv6CPTf4baKix4mA xen-2.4.16/include/xeno/blkdev.h
3ddb79c18ePBgitnOs7GiOCFilODVw xen-2.4.16/include/xeno/blkpg.h
3ddb79c2SisDOHDyTeK5-MV3m7pNbA xen-2.4.16/include/xeno/block.h
3ddb79c2JOriBs0mWh-Tlolq78tg3w xen-2.4.16/include/xeno/bootmem.h
3ddb79c1oOjpQbp68MW7yiUpoi-S-w xen-2.4.16/include/xeno/brlock.h
3ddb79c1x7Ie3kifu7dQRx8y7HVyvA xen-2.4.16/include/xeno/byteorder/big_endian.h
3ddb79c1qFXOEX1eD0yXJ_gsGkUt8w xen-2.4.16/include/xeno/byteorder/generic.h
3ddb79c1VbwFALNpgx6uC_iZKFHD-A xen-2.4.16/include/xeno/byteorder/little_endian.h
3ddb79c1VvNRMM35bpdZMekirCXP-A xen-2.4.16/include/xeno/byteorder/pdp_endian.h
3ddb79c116WbJV8bwGZXFFJy_GNNvw xen-2.4.16/include/xeno/byteorder/swab.h
3ddb79c1pwmlw8VXW8aaSKAVGVmjDA xen-2.4.16/include/xeno/byteorder/swabb.h
3ddb79c0c0cX_DZE209-Bb-Rx1v-Aw xen-2.4.16/include/xeno/cache.h
3ddb79c259jh8hE7vre_8NuE7nwNSA xen-2.4.16/include/xeno/config.h
3ddb79c1V44RD26YqCUm-kqIupM37A xen-2.4.16/include/xeno/ctype.h
3ddb79c05DdHQ0UxX_jKsXdR4QlMCA xen-2.4.16/include/xeno/delay.h
3ddb79c2PMeWTK86y4C3F4MzHw4A1g xen-2.4.16/include/xeno/dom0_ops.h
3ddb79c1uaWQZj551j1O0B5z8AnHOg xen-2.4.16/include/xeno/elevator.h
3ddb79c0HIghfBF8zFUdmXhOU8i6hA xen-2.4.16/include/xeno/errno.h
3ddb79c0rMjudDKkJku_mkm0J-BZgw xen-2.4.16/include/xeno/etherdevice.h
3ddb79c0T3X07lFnM9OSE-W5bqIDSQ xen-2.4.16/include/xeno/ethtool.h
3ddb79c1W0lQca8gRV7sN6j3iY4Luw xen-2.4.16/include/xeno/event.h
3ddb79c1J4I_AjNflZL-1c1jOIlSyg xen-2.4.16/include/xeno/genhd.h
3ddb79c1i-chIoeniqgYwMM3EgaR5w xen-2.4.16/include/xeno/hdreg.h
3ddb79c12GuUuaxBKiMuwf-Qvuwpng xen-2.4.16/include/xeno/hdsmart.h
3ddb79c0MM575N4YvMSiw9EqKH4JDA xen-2.4.16/include/xeno/ide.h
3ddb79c1yHLp08JhgPxIMcZ8DwN9hg xen-2.4.16/include/xeno/if.h
3ddb79c1RCWOkWPQRzbYVTX_e-E7CA xen-2.4.16/include/xeno/if_ether.h
3ddb79c2IYah7z7hkzPyOiG8szKkyw xen-2.4.16/include/xeno/if_packet.h
3ddb79c0GurNF9tDWqQbAwJFH8ugfA xen-2.4.16/include/xeno/init.h
3ddb79c1Vi5VleJAOKHAlY0G2zAsgw xen-2.4.16/include/xeno/interrupt.h
3ddb79c2J6EnruiygRhBCgftzMzTeQ xen-2.4.16/include/xeno/ioctl.h
3ddb79c1nzaWu8NoF4xCCMSFJR4MlA xen-2.4.16/include/xeno/ioport.h
3ddb79c2qAxCOABlkKtD8Txohe-qEw xen-2.4.16/include/xeno/irq.h
3ddb79c2b3qe-6Ann09FqZBF4IrJaQ xen-2.4.16/include/xeno/irq_cpustat.h
3ddb79c11w_O7z7YZJnzuDSxaK5LlA xen-2.4.16/include/xeno/kdev_t.h
3ddb79c1NfYlOrWNqgZkj9EwtFfJow xen-2.4.16/include/xeno/lib.h
3ddb79c18Ajy7micDGQQfJ0zWgEHtA xen-2.4.16/include/xeno/list.h
3ddb79c0_s2_wgV0cA6tztEaeyy1NA xen-2.4.16/include/xeno/major.h
3ddb79c1fsWuKI2sGlW5bqoG2lPVNA xen-2.4.16/include/xeno/mii.h
3ddb79c1gs2VbLbQlw0dcDUXYIepDA xen-2.4.16/include/xeno/mm.h
3ddb79c13p9iHn1XAp0IS1qvj4yDsg xen-2.4.16/include/xeno/module.h
3ddb79c1ieLZfGSFwfvvSQ2NK1BMSg xen-2.4.16/include/xeno/multiboot.h
3ddb79c0CLfAlJLg1ohdPD-Jjn-jxg xen-2.4.16/include/xeno/netdevice.h
3ddb79c2Fg44_PBPVxHSC0gTOMq4Ow xen-2.4.16/include/xeno/pci.h
3ddb79c0MOVXq8qZDQRGb6z64_xAwg xen-2.4.16/include/xeno/pci_ids.h
3ddb79c2byJwwNNkiES__A9H4Cvc4g xen-2.4.16/include/xeno/pkt_sched.h
3ddb79c04nQVR3EYM5L4zxDV_MCo1g xen-2.4.16/include/xeno/prefetch.h
3ddb79c0LzqqS0LhAQ50ekgj4oGl7Q xen-2.4.16/include/xeno/sched.h
3ddb79c0VDeD-Oft5eNfMneTU3D1dQ xen-2.4.16/include/xeno/skbuff.h
3ddb79c14dXIhP7C2ahnoD08K90G_w xen-2.4.16/include/xeno/slab.h
3ddb79c09xbS-xxfKxuV3JETIhBzmg xen-2.4.16/include/xeno/smp.h
3ddb79c1-yIt89RT02wIPp2xDR8YjQ xen-2.4.16/include/xeno/socket.h
3ddb79c2V2P9F2xMCzDJ9vbUofSg_Q xen-2.4.16/include/xeno/sockios.h
3ddb79c2iIcESrDAB8samy_yAh6olQ xen-2.4.16/include/xeno/spinlock.h
3ddb79c0BnA20PbgmuMPSGIBljNRQw xen-2.4.16/include/xeno/time.h
3ddb79c2HFkXuRxi1CriJtSFmY6Ybw xen-2.4.16/include/xeno/timer.h
3ddb79c2_m8lT9jDKse_tePj7zcnNQ xen-2.4.16/include/xeno/timex.h
3ddb79c2e2C14HkndNEJlYwXaPrF5A xen-2.4.16/include/xeno/tqueue.h
3ddb79c1-kVvF8cVa0k3ZHDdBMj01Q xen-2.4.16/include/xeno/types.h
3ddb79c2Ae5KpzhC9LCYG7mP_Vi4Aw xen-2.4.16/include/xeno/vif.h
3ddb79c4YQCQ6r0xNLLu0jfbM7pVmA xen-2.4.16/net/Makefile
3ddb79c4AkfDkTCw0comx4L8wsUOMg xen-2.4.16/net/dev.c
3ddb79c4x1L_soh8b-r_1jQW_37Icw xen-2.4.16/net/dev_mcast.c
3ddb79c4NSDwiQ-AmrYdxcRAwLPzwQ xen-2.4.16/net/eth.c
3ddb79c4KZhNxUuYJ7lul8cc-wRkyg xen-2.4.16/net/sch_generic.c
3ddb79c4TZj1wXPKQt36O72SddtBNQ xen-2.4.16/net/skbuff.c
3ddb79c4ARyIHqv3Y6YFckIUbyA8Tw xen-2.4.16/net/utils.c
3ddb79c4x8dvwPtzclghWAKFWpEBFA xen-2.4.16/tools/Makefile
3ddb79c4yGZ7_22QAFFwPzqP4NSHwA xen-2.4.16/tools/elf-reloc.c
3ddb79bbYMXGmQTsr5BeGS_RuZ5f_w xenolinux-2.4.16-sparse/Makefile
3ddb79b7e0ssyz3Q1GoqjDds-x1PLQ xenolinux-2.4.16-sparse/arch/xeno/Makefile
3ddb79b7_rLvYZU3tOY6Wwuw_Sg3_w xenolinux-2.4.16-sparse/arch/xeno/boot/Makefile
3ddb79b8L4xnwrcvWk6nAbgKVbNkSA xenolinux-2.4.16-sparse/arch/xeno/config.in
3ddb79b7v_Be34as7_mlzFlw65hOjQ xenolinux-2.4.16-sparse/arch/xeno/defconfig
3ddb79b7KUvtx0knQJoRaBDZQeNidg xenolinux-2.4.16-sparse/arch/xeno/drivers/block/Makefile
3ddb79b6Rc0uAOGFthIFxq1KGWZ_Iw xenolinux-2.4.16-sparse/arch/xeno/drivers/block/block.c
3ddb79b7LLVJBGynxHSOh9A9l97sug xenolinux-2.4.16-sparse/arch/xeno/drivers/console/Makefile
3ddb79b7UG2QiRAU-Wvc1Y_BLigu1Q xenolinux-2.4.16-sparse/arch/xeno/drivers/console/console.c
3ddb79b75eo4PRXkT6Th9popt_SJhg xenolinux-2.4.16-sparse/arch/xeno/drivers/dom0/Makefile
3ddb79b7Xyaoep6U0kLvx6Kx7OauDw xenolinux-2.4.16-sparse/arch/xeno/drivers/dom0/dom0_core.c
3ddb79b7PulSkF9m3c7K5MkxHRf4hA xenolinux-2.4.16-sparse/arch/xeno/drivers/dom0/dom0_ops.h
3ddb79b7s7yYBioHidSkIoHtQxYmOw xenolinux-2.4.16-sparse/arch/xeno/drivers/network/Makefile
3ddb79b7CpLL98ScdpbKkVBktlbCtQ xenolinux-2.4.16-sparse/arch/xeno/drivers/network/network.c
3ddb79b7hqi9krq6h98lnpONHGzvEA xenolinux-2.4.16-sparse/arch/xeno/kernel/Makefile
3ddb79b7eyEv5bsN8EQkjIG0y11Q1A xenolinux-2.4.16-sparse/arch/xeno/kernel/entry.S
3ddb79b70XAg9bJwp0-DWHe0LtzlBw xenolinux-2.4.16-sparse/arch/xeno/kernel/head.S
3ddb79b7dDsKjU22VxQ-C5BMFaMUmw xenolinux-2.4.16-sparse/arch/xeno/kernel/hypervisor.c
3ddb79b7xzwEc8-lo1vu3BxB-gBURQ xenolinux-2.4.16-sparse/arch/xeno/kernel/i386_ksyms.c
3ddb79b7i7pfPEcy_zjDfW9JHD305g xenolinux-2.4.16-sparse/arch/xeno/kernel/i387.c
3ddb79b7Ti2i5ztQzM-w67zN-cJD8A xenolinux-2.4.16-sparse/arch/xeno/kernel/init_task.c
3ddb79b7MEQGMZrsF94atNJZ4-OGzA xenolinux-2.4.16-sparse/arch/xeno/kernel/ioport.c
3ddb79b7DOz-Mz5jsjRd5W8jN0XbPw xenolinux-2.4.16-sparse/arch/xeno/kernel/irq.c
3ddb79b7bOAPk_YAUUsruhVGO2GOOg xenolinux-2.4.16-sparse/arch/xeno/kernel/ldt.c
3ddb79b7qf2WK6vMKcmOLIeKN5GSjg xenolinux-2.4.16-sparse/arch/xeno/kernel/process.c
3ddb79b7HdaBR7yk_u51auihbr31aQ xenolinux-2.4.16-sparse/arch/xeno/kernel/ptrace.c
3ddb79b7yJunvnrgWD2VTy6yot0PMg xenolinux-2.4.16-sparse/arch/xeno/kernel/semaphore.c
3ddb79b7BIitpVygiksiMBQYvh5Z2A xenolinux-2.4.16-sparse/arch/xeno/kernel/setup.c
3ddb79b7DTevmwhNla67jZxjBSIKFg xenolinux-2.4.16-sparse/arch/xeno/kernel/signal.c
3ddb79b76mCf-gZPR4KLjL8ktZ37GA xenolinux-2.4.16-sparse/arch/xeno/kernel/sys_i386.c
3ddb79b7xDsEKErRFeqcSm6eRrTEIg xenolinux-2.4.16-sparse/arch/xeno/kernel/time.c
3ddb79b7V4dv_KAQu4Msa2Ebhd0aKw xenolinux-2.4.16-sparse/arch/xeno/kernel/traps.c
3ddb79b8oUnwncDaZuRWF3-n3jPsIA xenolinux-2.4.16-sparse/arch/xeno/lib/Makefile
3ddb79b8BMxi8qW3_NT44SSd1uMD3Q xenolinux-2.4.16-sparse/arch/xeno/lib/checksum.S
3ddb79b8WcWel2g9zU9pBJb-yA8jBw xenolinux-2.4.16-sparse/arch/xeno/lib/dec_and_lock.c
3ddb79b8oxeiaIW6Au95OM0GlsMrMw xenolinux-2.4.16-sparse/arch/xeno/lib/delay.c
3ddb79b8XPasnRhvK-_6xYksf3S6qA xenolinux-2.4.16-sparse/arch/xeno/lib/getuser.S
3ddb79b8HFLUh8mwcl4X44ta-ny1KA xenolinux-2.4.16-sparse/arch/xeno/lib/iodebug.c
3ddb79b879qeoLlarHXvNIDEva6ssA xenolinux-2.4.16-sparse/arch/xeno/lib/memcpy.c
3ddb79b869CY_yr4HymV6k98pfpMgg xenolinux-2.4.16-sparse/arch/xeno/lib/mmx.c
3ddb79b8S77yf1--Qa4C0ZYmqKXCww xenolinux-2.4.16-sparse/arch/xeno/lib/old-checksum.c
3ddb79b8ffZ79cU2ZzfA2ekeo6pqeg xenolinux-2.4.16-sparse/arch/xeno/lib/strstr.c
3ddb79b82kQ5oIXpxq3TUmlgxsLzLg xenolinux-2.4.16-sparse/arch/xeno/lib/usercopy.c
3ddb79b8qdD_svLCCAja_oP2w4Tn8Q xenolinux-2.4.16-sparse/arch/xeno/mm/Makefile
3ddb79b8ukY8dsPYmR8eNk-aCzFPsQ xenolinux-2.4.16-sparse/arch/xeno/mm/extable.c
3ddb79b856Zta9b3s0bgUCGbG1blvQ xenolinux-2.4.16-sparse/arch/xeno/mm/fault.c
3ddb79b85fpsKT8A9WYnuJg03b715g xenolinux-2.4.16-sparse/arch/xeno/mm/hypervisor.c
3ddb79b83Zj7Xn2QVhU4HeMuAC9FjA xenolinux-2.4.16-sparse/arch/xeno/mm/init.c
3ddb79b7aKdTkbr3u6aze8tVwGh_TQ xenolinux-2.4.16-sparse/arch/xeno/vmlinux.lds
3ddb79bbx682YH6vR2zbVOXwg73ULg xenolinux-2.4.16-sparse/drivers/block/ll_rw_blk.c
3ddb79bcJfHdwrPsjqgI33_OsGdVCg xenolinux-2.4.16-sparse/drivers/block/rd.c
3ddb79bcpVu-IbnqwQqpRqsEbLpsuw xenolinux-2.4.16-sparse/drivers/char/tty_io.c
3ddb79bba_zKpuurHVeWfgDkyPoq8A xenolinux-2.4.16-sparse/fs/nfs/nfsroot.c
3ddb79b8VFtfWSCrXKPN2K21zd_vtw xenolinux-2.4.16-sparse/include/asm-xeno/a.out.h
3ddb79b8Zzi13p3OAPV25QgiC3THAQ xenolinux-2.4.16-sparse/include/asm-xeno/apic.h
3ddb79baZDlsdV_m6C5CXnWMl15p1g xenolinux-2.4.16-sparse/include/asm-xeno/apicdef.h
3ddb79baZM88u4CnriVA8ZXBdnMNvg xenolinux-2.4.16-sparse/include/asm-xeno/atomic.h
3ddb79baYHyZsDCiXiq8Y8_XxHE-jQ xenolinux-2.4.16-sparse/include/asm-xeno/bitops.h
3ddb79b8vFGtGb6pg3GZFXSiwOZfcg xenolinux-2.4.16-sparse/include/asm-xeno/boot.h
3ddb79baW8tf6PiBQUF50QQM5nY9sw xenolinux-2.4.16-sparse/include/asm-xeno/bugs.h
3ddb79b80msOlzTZRoVudYdemzgOlA xenolinux-2.4.16-sparse/include/asm-xeno/byteorder.h
3ddb79b8brNSUEujnq8f_zr8kA-cUg xenolinux-2.4.16-sparse/include/asm-xeno/cache.h
3ddb79bayhr6C6prVhAYlFRChhf3wg xenolinux-2.4.16-sparse/include/asm-xeno/checksum.h
3ddb79b8RNUaDbpPjdVVwKAsbiTBKQ xenolinux-2.4.16-sparse/include/asm-xeno/cpufeature.h
3ddb79b8pJe4aNsUKkfHEoBT9Y-UMA xenolinux-2.4.16-sparse/include/asm-xeno/current.h
3ddb79b8KL7icUfxKRoWDIkHkLQ1kQ xenolinux-2.4.16-sparse/include/asm-xeno/debugreg.h
3ddb79baDUP_cRdFgqaH0rXUvMxx4A xenolinux-2.4.16-sparse/include/asm-xeno/delay.h
3ddb79b89CgBTFsS3joEJ1ZniSHEgA xenolinux-2.4.16-sparse/include/asm-xeno/desc.h
3ddb79ba6xyT4mJOYSp1Fg2l0ta93A xenolinux-2.4.16-sparse/include/asm-xeno/div64.h
3ddb79b80Z4ZUIqbD1Xu_t4OCuEHeQ xenolinux-2.4.16-sparse/include/asm-xeno/dma.h
3ddb79bac26NkKcPIEsfxETc5Snyag xenolinux-2.4.16-sparse/include/asm-xeno/elf.h
3ddb79ba722pCJ_g_xI8ebsE31IK-Q xenolinux-2.4.16-sparse/include/asm-xeno/errno.h
3ddb79b8vIpUpgaSNEneFkg5hYSvNg xenolinux-2.4.16-sparse/include/asm-xeno/fcntl.h
3ddb79b8c_oKu2_BGNJctM4DBET31Q xenolinux-2.4.16-sparse/include/asm-xeno/fixmap.h
3ddb79b8780YvqvK1g5KPIWzQ6P15w xenolinux-2.4.16-sparse/include/asm-xeno/floppy.h
3ddb79bas-nFywnmilbUeT34PEAA0g xenolinux-2.4.16-sparse/include/asm-xeno/hardirq.h
3ddb79batzR40ZFY9dvgs5f1aM9I6g xenolinux-2.4.16-sparse/include/asm-xeno/hdreg.h
3ddb79b90xBgbeYgCcImS2ZxJakxBA xenolinux-2.4.16-sparse/include/asm-xeno/highmem.h
3ddb79baXLZV3dUKQI2gIYpAy67RuA xenolinux-2.4.16-sparse/include/asm-xeno/hw_irq.h
3ddb79b82xfEY3yBet-2FXY4p8b7yg xenolinux-2.4.16-sparse/include/asm-xeno/hypervisor-ifs/block.h
3ddb79b8KUSolAgH19qEzo1Ey0f1Ng xenolinux-2.4.16-sparse/include/asm-xeno/hypervisor-ifs/hypervisor-if.h
3ddb79b8J0Y2UA8NKoN5Ng71WFQRIg xenolinux-2.4.16-sparse/include/asm-xeno/hypervisor-ifs/network.h
3ddb79bapQ9Z9ewa5O1pqAVaNBTazg xenolinux-2.4.16-sparse/include/asm-xeno/hypervisor.h
3ddb79baL-pjPI8hg5xjPgd4__SlOA xenolinux-2.4.16-sparse/include/asm-xeno/i387.h
3ddb79ba66TwvG7HpbBo04fRhmj3KQ xenolinux-2.4.16-sparse/include/asm-xeno/ide.h
3ddb79bahFfCuRcmd9kBYA-CuGtCSg xenolinux-2.4.16-sparse/include/asm-xeno/init.h
3ddb79ba3wnwflaNW6QheYvxoj5S8Q xenolinux-2.4.16-sparse/include/asm-xeno/io.h
3ddb79ba1MhHpElCXFlijej2zWVk4g xenolinux-2.4.16-sparse/include/asm-xeno/io_apic.h
3ddb79baiyfcZN9rJwhq0UeFjI6GkQ xenolinux-2.4.16-sparse/include/asm-xeno/ioctl.h
3ddb79badReKYSok3yHShb4jg0vA-A xenolinux-2.4.16-sparse/include/asm-xeno/ioctls.h
3ddb79b9iuIxteTXg8_myIsrWF5uxg xenolinux-2.4.16-sparse/include/asm-xeno/ipc.h
3ddb79baw5Dxo78880UMSanDV70WdA xenolinux-2.4.16-sparse/include/asm-xeno/ipcbuf.h
3ddb79ban9FUBuEaznRZvPfry3xnHQ xenolinux-2.4.16-sparse/include/asm-xeno/irq.h
3ddb79banJ5r-mqE7LZ8nPGTADaGBA xenolinux-2.4.16-sparse/include/asm-xeno/kdb.h
3ddb79baoaoHGTW0oiBXFPUg4KWydw xenolinux-2.4.16-sparse/include/asm-xeno/kdbprivate.h
3ddb79bam2K6c7tS2HX6jis2Bqx71w xenolinux-2.4.16-sparse/include/asm-xeno/keyboard.h
3ddb79baQ8gGrZm2Jlo3I1ntD0H5HA xenolinux-2.4.16-sparse/include/asm-xeno/kmap_types.h
3ddb79b9MeQEYrafy-Mx9OoeVFM_uw xenolinux-2.4.16-sparse/include/asm-xeno/ldt.h
3ddb79baAsy5W-cJ9ML_w9chqqUh4A xenolinux-2.4.16-sparse/include/asm-xeno/linux_logo.h
3ddb79b9uhsumPVE0wGgarfIkYDSFA xenolinux-2.4.16-sparse/include/asm-xeno/locks.h
3ddb79b954ISbF9e68hB0WTulCJRgg xenolinux-2.4.16-sparse/include/asm-xeno/math_emu.h
3ddb79b9EZDlC6RGn_y0OYr0nyQWlw xenolinux-2.4.16-sparse/include/asm-xeno/mc146818rtc.h
3ddb79b9M6pTF4maDgh8TYbg_HHUbw xenolinux-2.4.16-sparse/include/asm-xeno/mca_dma.h
3ddb79baA7dlps8FkicOUEXKEQuQsA xenolinux-2.4.16-sparse/include/asm-xeno/mman.h
3ddb79baKfF36-eRvkxnEvMRQRai-w xenolinux-2.4.16-sparse/include/asm-xeno/mmu.h
3ddb79baQyKbT5U4EmZNePY9Txp-tA xenolinux-2.4.16-sparse/include/asm-xeno/mmu_context.h
3ddb79bberC3Ghs4vy-06Pu-LSiWtw xenolinux-2.4.16-sparse/include/asm-xeno/mmx.h
3ddb79bbsJLF10xQcKDoV8f_7gcOXg xenolinux-2.4.16-sparse/include/asm-xeno/module.h
3ddb79bbY5RffJ8_F1oC4VI7q3Eejg xenolinux-2.4.16-sparse/include/asm-xeno/mpspec.h
3ddb79b90vB4Vzzy_wL6SYXQMy9N9Q xenolinux-2.4.16-sparse/include/asm-xeno/msgbuf.h
3ddb79bbZ5a_vTk0xcgOHuPokaqwMw xenolinux-2.4.16-sparse/include/asm-xeno/msr.h
3ddb79b9y1xAKaPiBd79MBcCopNNYw xenolinux-2.4.16-sparse/include/asm-xeno/mtrr.h
3ddb79b90hX0QBJlWQN_VsHtX5Ijkw xenolinux-2.4.16-sparse/include/asm-xeno/namei.h
3ddb79bbG2p9MNq7tuISz8md1Oj2lg xenolinux-2.4.16-sparse/include/asm-xeno/page.h
3ddb79bb_iUa2piFSwaB8YPw-rB5SQ xenolinux-2.4.16-sparse/include/asm-xeno/param.h
3ddb79b9Y8UU0S9AoDznoqqcYxg9-A xenolinux-2.4.16-sparse/include/asm-xeno/parport.h
3ddb79b9K9_edWxBHS7TdCpyDmfp6g xenolinux-2.4.16-sparse/include/asm-xeno/pgalloc.h
3ddb79bahaS_P3UYp9VEU6kHxXbajA xenolinux-2.4.16-sparse/include/asm-xeno/pgtable-2level.h
3ddb79b9MjXUB_rk29GJgaNY24feCw xenolinux-2.4.16-sparse/include/asm-xeno/pgtable-3level.h
3ddb79bbPF2ENpNHBru8K3hyYVPmkQ xenolinux-2.4.16-sparse/include/asm-xeno/pgtable.h
3ddb79b9t9xKrOf8aP3X5jEit9tj-w xenolinux-2.4.16-sparse/include/asm-xeno/poll.h
3ddb79b9xHtTDWFaI9ncAtxyUth0Yg xenolinux-2.4.16-sparse/include/asm-xeno/posix_types.h
3ddb79b9VwZ9YsSpA7CkJmYXVadrCA xenolinux-2.4.16-sparse/include/asm-xeno/processor.h
3ddb79bbKaOkHrGG9j05AGlii-voaQ xenolinux-2.4.16-sparse/include/asm-xeno/ptrace.h
3ddb79bbVpJCVTXmc2yNf1rsC00YEg xenolinux-2.4.16-sparse/include/asm-xeno/resource.h
3ddb79bbtU0Kh27NbNpqKAIhshJvXQ xenolinux-2.4.16-sparse/include/asm-xeno/rwlock.h
3ddb79b97LhdmW6hYmybQOPDkK6plg xenolinux-2.4.16-sparse/include/asm-xeno/rwsem.h
3ddb79bbgRzM1NoXYbLoOCbZt8s5NA xenolinux-2.4.16-sparse/include/asm-xeno/scatterlist.h
3ddb79b9xCi3F80Z6xxx6nzkCAo8vQ xenolinux-2.4.16-sparse/include/asm-xeno/segment.h
3ddb79b9WxySbCKDoMgTgPtn1G3BFw xenolinux-2.4.16-sparse/include/asm-xeno/semaphore.h
3ddb79b9gNK3dtgXONloSBaNgZPjAg xenolinux-2.4.16-sparse/include/asm-xeno/sembuf.h
3ddb79bb2UTDgfwju2J3weDXemw3LA xenolinux-2.4.16-sparse/include/asm-xeno/serial.h
3ddb79bbAPFyA_n4dNVCRoee8obOKA xenolinux-2.4.16-sparse/include/asm-xeno/setup.h
3ddb79b9XTOB5DwWBGzPLLR4rNEkDQ xenolinux-2.4.16-sparse/include/asm-xeno/shmbuf.h
3ddb79b9-_jDlAj3qVZe4opi3zectQ xenolinux-2.4.16-sparse/include/asm-xeno/shmparam.h
3ddb79b9AW75ErwlTRX4McxO15sEaQ xenolinux-2.4.16-sparse/include/asm-xeno/sigcontext.h
3ddb79b9NBJW-KAI3mgveUCr7sIOwA xenolinux-2.4.16-sparse/include/asm-xeno/siginfo.h
3ddb79bbj0i8tUVNMKtZVrLJqv3Nsw xenolinux-2.4.16-sparse/include/asm-xeno/signal.h
3ddb79bbfAmpotdy-No2dwGez2fnIg xenolinux-2.4.16-sparse/include/asm-xeno/smp.h
3ddb79b9pDERXiqSumFWMTFJ1X9xIw xenolinux-2.4.16-sparse/include/asm-xeno/smplock.h
3ddb79bbuCOIWTlWEHgOTexEBbdDow xenolinux-2.4.16-sparse/include/asm-xeno/socket.h
3ddb79b9ExeUznVBlSn1e2nvOCrJ4A xenolinux-2.4.16-sparse/include/asm-xeno/sockios.h
3ddb79b9kL3xvucBb-Gmg4_vo-99vw xenolinux-2.4.16-sparse/include/asm-xeno/softirq.h
3ddb79b9rJ8AfSzGzA0arI8mazYLlQ xenolinux-2.4.16-sparse/include/asm-xeno/spinlock.h
3ddb79bbXaA_zUHNPkAKRNz1h0gIJw xenolinux-2.4.16-sparse/include/asm-xeno/stat.h
3ddb79b9G004IlCplrjWgF1aXbp8dA xenolinux-2.4.16-sparse/include/asm-xeno/statfs.h
3ddb79bbRsy3GlCFrQEbVMVp--xlwQ xenolinux-2.4.16-sparse/include/asm-xeno/string-486.h
3ddb79bb4xug4cDph6ODLQFQIan_sg xenolinux-2.4.16-sparse/include/asm-xeno/string.h
3ddb79b9JhjJtJUO3g5LmrHPkdxgKg xenolinux-2.4.16-sparse/include/asm-xeno/system.h
3ddb79b9tbjCU9zSbqKbbv4m8tijlg xenolinux-2.4.16-sparse/include/asm-xeno/termbits.h
3ddb79bbi0mW10tH4xX1_KHXKM_xPg xenolinux-2.4.16-sparse/include/asm-xeno/termios.h
3ddb79b9JuR1VvNzlkyMlA-Dnlmy9Q xenolinux-2.4.16-sparse/include/asm-xeno/timex.h
3ddb79b9Bofq-p3sCTF0ELVuf_iBYA xenolinux-2.4.16-sparse/include/asm-xeno/tlb.h
3ddb79b9tpBUqS8-S6euSqyk2hFkKg xenolinux-2.4.16-sparse/include/asm-xeno/types.h
3ddb79bb5bkAaEzD7pdqQZdWyA_0eQ xenolinux-2.4.16-sparse/include/asm-xeno/uaccess.h
3ddb79bbiDIz1dxgFixHKyGuqRqfDQ xenolinux-2.4.16-sparse/include/asm-xeno/ucontext.h
3ddb79ba_Smn-GiYtr5ZTMaZXn-AHg xenolinux-2.4.16-sparse/include/asm-xeno/unaligned.h
3ddb79bb3cMSs_k2X5Oq2hOIBvmPYA xenolinux-2.4.16-sparse/include/asm-xeno/unistd.h
3ddb79ba2qYtIQAT_-vCFkkZUXu_UQ xenolinux-2.4.16-sparse/include/asm-xeno/user.h
3ddb79bbqhb9X9qWOz5Bv4wOzrkITg xenolinux-2.4.16-sparse/include/asm-xeno/vga.h
3ddb79bbA52x94o6uwDYsbzrH2hjzA xenolinux-2.4.16-sparse/include/asm-xeno/xor.h
3ddb79bb_7YG4U75ZmEic9YXWTW7Vw xenolinux-2.4.16-sparse/include/linux/sunrpc/debug.h
3ddb79bcxkVPfWlZ1PQKvDrfArzOVw xenolinux-2.4.16-sparse/kernel/panic.c
3ddb79bbP31im-mx2NbfthSeqty1Dg xenolinux-2.4.16-sparse/mk
+1
View File
@@ -0,0 +1 @@
smh22@boulderdash.cl.cam.ac.uk
+39
View File
@@ -0,0 +1,39 @@
export BASEDIR := $(shell pwd)
include Rules.mk
default: $(TARGET)
install: $(TARGET)
gzip -f -9 < $(TARGET) > $(TARGET).gz
cp $(TARGET).gz ../../install/images/image
clean: delete-links
$(MAKE) -C tools clean
$(MAKE) -C common clean
$(MAKE) -C net clean
$(MAKE) -C drivers clean
$(MAKE) -C arch/$(ARCH) clean
rm -f *.o $(TARGET)* *~ core
$(TARGET): make-links
$(MAKE) -C tools
$(MAKE) -C common
$(MAKE) -C net
$(MAKE) -C drivers
$(MAKE) -C arch/$(ARCH)
make-links:
ln -sf xeno include/linux
ln -sf asm-$(ARCH) include/asm
delete-links:
rm -f include/linux include/asm
SUBDIRS =arch common drivers net
TAGS:
etags `find include/asm-$(ARCH) -name '*.h'`
find include -type d \( -name "asm-*" -o -name config \) -prune -o -name '*.h' -print | xargs etags -a
find $(SUBDIRS) -name '*.[ch]' | xargs etags -a
+145
View File
@@ -0,0 +1,145 @@
*****************************************************
Xeno Hypervisor (18/7/02)
1) Tree layout
Looks rather like a simplified Linux :-)
Headers are in include/xeno and include asm-<arch>.
At build time we create symlinks:
include/linux -> include/xeno
include/asm -> include/asm-<arch>
In this way, Linux device drivers should need less tweaking of
their #include lines.
For source files, mapping between hypervisor and Linux is:
Linux Hypervisor
----- ----------
kernel/init/mm/lib -> common
net/* -> net/*
drivers/* -> drivers/*
arch/* -> arch/*
Note that the use of #include <asm/...> and #include <linux/...> can
lead to confusion, as such files will often exist on the system include
path, even if a version doesn't exist within the hypervisor tree.
Unfortunately '-nostdinc' cannot be specified to the compiler, as that
prevents us using stdarg.h in the compiler's own header directory.
We try to not modify things in driver/* as much as possible, so we can
easily take updates from Linux. arch/* is basically straight from
Linux, with fingers in Linux-specific pies hacked off. common/* has
a lot of Linux code in it, but certain subsystems (task maintenance,
low-level memory handling) have been replaced. net/* contains enough
Linux-like gloop to get network drivers to work with little/no
modification.
2) Building
'make': Builds ELF executable called 'image' in base directory
'make install': gzip-compresses 'image' and copies it to TFTP server
'make clean': removes *all* build and target files
*****************************************************
Random thoughts and stuff from here down...
Todo list
---------
* Hypervisor need only directly map its own memory pool
(maybe 128MB, tops). That would need 0x08000000....
This would allow 512MB Linux with plenty room for vmalloc'ed areas.
* Network device -- port drivers to hypervisor, implement virtual
driver for xeno-linux. Looks like Ethernet.
-- Hypervisor needs to do (at a minimum):
- packet filtering on tx (unicast IP only)
- packet demux on rx (unicast IP only)
- provide DHCP [maybedo something simpler?]
and ARP [at least for hypervisor IP address]
Segment descriptor tables
-------------------------
We want to allow guest OSes to specify GDT and LDT tables using their
own pages of memory (just like with page tables). So allow the following:
* new_table_entry(ptr, val)
[Allows insertion of a code, data, or LDT descriptor into given
location. Can simply be checked then poked, with no need to look at
page type.]
* new_GDT() -- relevent virtual pages are resolved to frames. Either
(i) page not present; or (ii) page is only mapped read-only and checks
out okay (then marked as special page). Old table is resolved first,
and the pages are unmarked (no longer special type).
* new_LDT() -- same as for new_GDT(), with same special page type.
Page table updates must be hooked, so we look for updates to virtual page
addresses in the GDT/LDT range. If map to not present, then old physpage
has type_count decremented. If map to present, ensure read-only, check the
page, and set special type.
Merge set_{LDT,GDT} into update_baseptr, by passing four args:
update_baseptrs(mask, ptab, gdttab, ldttab);
Update of ptab requires update of gtab (or set to internal default).
Update of gtab requires update of ltab (or set to internal default).
The hypervisor page cache
-------------------------
This will allow guest OSes to make use of spare pages in the system, but
allow them to be immediately used for any new domains or memory requests.
The idea is that, when a page is laundered and falls off Linux's clean_LRU
list, rather than freeing it it becomes a candidate for passing down into
the hypervisor. In return, xeno-linux may ask for one of its previously-
cached pages back:
(page, new_id) = cache_query(page, old_id);
If the requested page couldn't be kept, a blank page is returned.
When would Linux make the query? Whenever it wants a page back without
the delay or going to disc. Also, whenever a page would otherwise be
flushed to disc.
To try and add to the cache: (blank_page, new_id) = cache_query(page, NULL);
[NULL means "give me a blank page"].
To try and retrieve from the cache: (page, new_id) = cache_query(x_page, id)
[we may request that x_page just be discarded, and therefore not impinge
on this domain's cache quota].
Booting secondary processors
----------------------------
start_of_day (i386/setup.c)
smp_boot_cpus (i386/smpboot.c)
* initialises boot CPU data
* parses APIC tables
* for each cpu:
do_boot_cpu (i386/smpboot.c)
* forks a new idle process
* points initial stack inside new task struct
* points initial EIP at a trampoline in very low memory
* frobs remote APIC....
On other processor:
* trampoline sets GDT and IDT
* jumps at main boot address with magic register value
* after setting proper page and descriptor tables, jumps at...
initialize_secondary (i386/smpboot.c)
* simply reads ESP/EIP out of the (new) idle task
* this causes a jump to...
start_secondary (i386/smpboot.c)
* reset all processor state
* barrier, then write bitmasks to signal back to boot cpu
* then barrel into...
cpu_idle (i386/process.c)
[THIS IS PROBABLY REASONABLE -- BOOT CPU SHOULD KICK
SECONDARIES TO GET WORK DONE]
SMP capabilities
----------------
Current intention is to allow hypervisor to schedule on all processors in
SMP boxen, but to tie each domain to a single processor. This simplifies
many SMP intricacies both in terms of correctness and efficiency (eg.
TLB flushing, network packet delivery, ...).
Clients can still make use of SMP by installing multiple domains on a single
machine, and treating it as a fast cluster (at the very least, the
hypervisor will have fast routing of locally-destined packets).
+31
View File
@@ -0,0 +1,31 @@
ARCH := i386
TARGET := $(BASEDIR)/image
HDRS := $(wildcard $(BASEDIR)/include/xeno/*.h)
HDRS += $(wildcard $(BASEDIR)/include/scsi/*.h)
HDRS += $(wildcard $(BASEDIR)/include/hypervisor-ifs/*.h)
HDRS += $(wildcard $(BASEDIR)/include/asm-$(ARCH)/*.h)
C_SRCS := $(wildcard *.c)
S_SRCS := $(wildcard *.S)
OBJS := $(patsubst %.S,%.o,$(S_SRCS))
OBJS += $(patsubst %.c,%.o,$(C_SRCS))
# Note that link order matters!
ALL_OBJS := $(BASEDIR)/common/common.o
ALL_OBJS += $(BASEDIR)/net/network.o
ALL_OBJS += $(BASEDIR)/drivers/pci/driver.o
ALL_OBJS += $(BASEDIR)/drivers/net/driver.o
ALL_OBJS += $(BASEDIR)/drivers/block/driver.o
ALL_OBJS += $(BASEDIR)/drivers/ide/driver.o
ALL_OBJS += $(BASEDIR)/arch/$(ARCH)/arch.o
include $(BASEDIR)/arch/$(ARCH)/Rules.mk
%.o: %.c $(HDRS) Makefile
$(CC) $(CFLAGS) -c $< -o $@
%.o: %.S $(HDRS) Makefile
$(CC) $(CFLAGS) -D__ASSEMBLY__ -c $< -o $@
+17
View File
@@ -0,0 +1,17 @@
include $(BASEDIR)/Rules.mk
# What happens here? We link monitor object files together, starting
# at MONITOR_BASE (a very high address). But bootloader cannot put
# things there, so we initially load at LOAD_BASE. A hacky little
# tool called `elf-reloc' is used to modify segment offsets from
# MONITOR_BASE-relative to LOAD_BASE-relative.
# (NB. Linux gets round this by turning its image into raw binary, then
# wrapping that with a low-memory bootstrapper.)
default: boot/boot.o $(OBJS)
$(LD) -r -o arch.o $(OBJS)
$(LD) $(LDFLAGS) boot/boot.o $(ALL_OBJS) -o $(TARGET)
$(BASEDIR)/tools/elf-reloc $(MONITOR_BASE) $(LOAD_BASE) $(TARGET)
clean:
rm -f *.o *~ core boot/*.o boot/*~ boot/core
+14
View File
@@ -0,0 +1,14 @@
########################################
# x86-specific definitions
CC := gcc
LD := ld
# Linker should relocate monitor to this address
MONITOR_BASE := 0xE0100000
# Bootloader should load monitor to this real address
LOAD_BASE := 0x00100000
CFLAGS := -fno-builtin -O3 -Wall -DMONITOR_BASE=$(MONITOR_BASE)
CFLAGS += -I$(BASEDIR)/include -D__KERNEL__
LDFLAGS := -T xeno.lds -N
+836
View File
@@ -0,0 +1,836 @@
/*
* Local APIC handling, local APIC timers
*
* (c) 1999, 2000 Ingo Molnar <mingo@redhat.com>
*
* Fixes
* Maciej W. Rozycki : Bits for genuine 82489DX APICs;
* thanks to Eric Gilmore
* and Rolf G. Tews
* for testing these extensively.
*/
#include <xeno/config.h>
#include <xeno/init.h>
#include <xeno/sched.h>
#include <xeno/irq.h>
#include <xeno/delay.h>
#include <asm/mc146818rtc.h>
#include <asm/msr.h>
#include <xeno/errno.h>
#include <asm/atomic.h>
#include <xeno/smp.h>
#include <xeno/interrupt.h>
#include <asm/mpspec.h>
#include <asm/pgalloc.h>
#include <asm/hardirq.h>
/* Using APIC to generate smp_local_timer_interrupt? */
int using_apic_timer = 0;
int get_maxlvt(void)
{
unsigned int v, ver, maxlvt;
v = apic_read(APIC_LVR);
ver = GET_APIC_VERSION(v);
/* 82489DXs do not report # of LVT entries. */
maxlvt = APIC_INTEGRATED(ver) ? GET_APIC_MAXLVT(v) : 2;
return maxlvt;
}
void clear_local_APIC(void)
{
int maxlvt;
unsigned long v;
maxlvt = get_maxlvt();
/*
* Careful: we have to set masks only first to deassert
* any level-triggered sources.
*/
v = apic_read(APIC_LVTT);
apic_write_around(APIC_LVTT, v | APIC_LVT_MASKED);
v = apic_read(APIC_LVT0);
apic_write_around(APIC_LVT0, v | APIC_LVT_MASKED);
v = apic_read(APIC_LVT1);
apic_write_around(APIC_LVT1, v | APIC_LVT_MASKED);
if (maxlvt >= 3) {
v = apic_read(APIC_LVTERR);
apic_write_around(APIC_LVTERR, v | APIC_LVT_MASKED);
}
if (maxlvt >= 4) {
v = apic_read(APIC_LVTPC);
apic_write_around(APIC_LVTPC, v | APIC_LVT_MASKED);
}
/*
* Clean APIC state for other OSs:
*/
apic_write_around(APIC_LVTT, APIC_LVT_MASKED);
apic_write_around(APIC_LVT0, APIC_LVT_MASKED);
apic_write_around(APIC_LVT1, APIC_LVT_MASKED);
if (maxlvt >= 3)
apic_write_around(APIC_LVTERR, APIC_LVT_MASKED);
if (maxlvt >= 4)
apic_write_around(APIC_LVTPC, APIC_LVT_MASKED);
}
void __init connect_bsp_APIC(void)
{
if (pic_mode) {
/*
* Do not trust the local APIC being empty at bootup.
*/
clear_local_APIC();
/*
* PIC mode, enable APIC mode in the IMCR, i.e.
* connect BSP's local APIC to INT and NMI lines.
*/
printk("leaving PIC mode, enabling APIC mode.\n");
outb(0x70, 0x22);
outb(0x01, 0x23);
}
}
void disconnect_bsp_APIC(void)
{
if (pic_mode) {
/*
* Put the board back into PIC mode (has an effect
* only on certain older boards). Note that APIC
* interrupts, including IPIs, won't work beyond
* this point! The only exception are INIT IPIs.
*/
printk("disabling APIC mode, entering PIC mode.\n");
outb(0x70, 0x22);
outb(0x00, 0x23);
}
}
void disable_local_APIC(void)
{
unsigned long value;
clear_local_APIC();
/*
* Disable APIC (implies clearing of registers
* for 82489DX!).
*/
value = apic_read(APIC_SPIV);
value &= ~APIC_SPIV_APIC_ENABLED;
apic_write_around(APIC_SPIV, value);
}
/*
* This is to verify that we're looking at a real local APIC.
* Check these against your board if the CPUs aren't getting
* started for no apparent reason.
*/
int __init verify_local_APIC(void)
{
unsigned int reg0, reg1;
/*
* The version register is read-only in a real APIC.
*/
reg0 = apic_read(APIC_LVR);
Dprintk("Getting VERSION: %x\n", reg0);
apic_write(APIC_LVR, reg0 ^ APIC_LVR_MASK);
reg1 = apic_read(APIC_LVR);
Dprintk("Getting VERSION: %x\n", reg1);
/*
* The two version reads above should print the same
* numbers. If the second one is different, then we
* poke at a non-APIC.
*/
if (reg1 != reg0)
return 0;
/*
* Check if the version looks reasonably.
*/
reg1 = GET_APIC_VERSION(reg0);
if (reg1 == 0x00 || reg1 == 0xff)
return 0;
reg1 = get_maxlvt();
if (reg1 < 0x02 || reg1 == 0xff)
return 0;
/*
* The ID register is read/write in a real APIC.
*/
reg0 = apic_read(APIC_ID);
Dprintk("Getting ID: %x\n", reg0);
apic_write(APIC_ID, reg0 ^ APIC_ID_MASK);
reg1 = apic_read(APIC_ID);
Dprintk("Getting ID: %x\n", reg1);
apic_write(APIC_ID, reg0);
if (reg1 != (reg0 ^ APIC_ID_MASK))
return 0;
/*
* The next two are just to see if we have sane values.
* They're only really relevant if we're in Virtual Wire
* compatibility mode, but most boxes are anymore.
*/
reg0 = apic_read(APIC_LVT0);
Dprintk("Getting LVT0: %x\n", reg0);
reg1 = apic_read(APIC_LVT1);
Dprintk("Getting LVT1: %x\n", reg1);
return 1;
}
void __init sync_Arb_IDs(void)
{
/*
* Wait for idle.
*/
apic_wait_icr_idle();
Dprintk("Synchronizing Arb IDs.\n");
apic_write_around(APIC_ICR, APIC_DEST_ALLINC | APIC_INT_LEVELTRIG
| APIC_DM_INIT);
}
extern void __error_in_apic_c (void);
/*
* An initial setup of the virtual wire mode.
*/
void __init init_bsp_APIC(void)
{
unsigned long value, ver;
/*
* Don't do the setup now if we have a SMP BIOS as the
* through-I/O-APIC virtual wire mode might be active.
*/
if (smp_found_config || !cpu_has_apic)
return;
value = apic_read(APIC_LVR);
ver = GET_APIC_VERSION(value);
/*
* Do not trust the local APIC being empty at bootup.
*/
clear_local_APIC();
/*
* Enable APIC.
*/
value = apic_read(APIC_SPIV);
value &= ~APIC_VECTOR_MASK;
value |= APIC_SPIV_APIC_ENABLED;
value |= APIC_SPIV_FOCUS_DISABLED;
value |= SPURIOUS_APIC_VECTOR;
apic_write_around(APIC_SPIV, value);
/*
* Set up the virtual wire mode.
*/
apic_write_around(APIC_LVT0, APIC_DM_EXTINT);
value = APIC_DM_NMI;
if (!APIC_INTEGRATED(ver)) /* 82489DX */
value |= APIC_LVT_LEVEL_TRIGGER;
apic_write_around(APIC_LVT1, value);
}
void __init setup_local_APIC (void)
{
unsigned long value, ver, maxlvt;
value = apic_read(APIC_LVR);
ver = GET_APIC_VERSION(value);
if ((SPURIOUS_APIC_VECTOR & 0x0f) != 0x0f)
__error_in_apic_c();
/* Double-check wether this APIC is really registered. */
if (!test_bit(GET_APIC_ID(apic_read(APIC_ID)), &phys_cpu_present_map))
BUG();
/*
* Intel recommends to set DFR, LDR and TPR before enabling
* an APIC. See e.g. "AP-388 82489DX User's Manual" (Intel
* document number 292116). So here it goes...
*/
/*
* In clustered apic mode, the firmware does this for us
* Put the APIC into flat delivery mode.
* Must be "all ones" explicitly for 82489DX.
*/
apic_write_around(APIC_DFR, 0xffffffff);
/*
* Set up the logical destination ID.
*/
value = apic_read(APIC_LDR);
value &= ~APIC_LDR_MASK;
value |= (1<<(smp_processor_id()+24));
apic_write_around(APIC_LDR, value);
/*
* Set Task Priority to 'accept all'. We never change this
* later on.
*/
value = apic_read(APIC_TASKPRI);
value &= ~APIC_TPRI_MASK;
apic_write_around(APIC_TASKPRI, value);
/*
* Now that we are all set up, enable the APIC
*/
value = apic_read(APIC_SPIV);
value &= ~APIC_VECTOR_MASK;
/*
* Enable APIC
*/
value |= APIC_SPIV_APIC_ENABLED;
/* Enable focus processor (bit==0) */
value &= ~APIC_SPIV_FOCUS_DISABLED;
/* Set spurious IRQ vector */
value |= SPURIOUS_APIC_VECTOR;
apic_write_around(APIC_SPIV, value);
/*
* Set up LVT0, LVT1:
*
* set up through-local-APIC on the BP's LINT0. This is not
* strictly necessery in pure symmetric-IO mode, but sometimes
* we delegate interrupts to the 8259A.
*/
/*
* TODO: set up through-local-APIC from through-I/O-APIC? --macro
*/
value = apic_read(APIC_LVT0) & APIC_LVT_MASKED;
if (!smp_processor_id()) {
/* && (pic_mode || !value)) { */
value = APIC_DM_EXTINT;
printk("enabled ExtINT on CPU#%d\n", smp_processor_id());
} else {
value = APIC_DM_EXTINT | APIC_LVT_MASKED;
printk("masked ExtINT on CPU#%d\n", smp_processor_id());
}
apic_write_around(APIC_LVT0, value);
/*
* only the BP should see the LINT1 NMI signal, obviously.
*/
if (!smp_processor_id())
value = APIC_DM_NMI;
else
value = APIC_DM_NMI | APIC_LVT_MASKED;
if (!APIC_INTEGRATED(ver)) /* 82489DX */
value |= APIC_LVT_LEVEL_TRIGGER;
apic_write_around(APIC_LVT1, value);
if (APIC_INTEGRATED(ver)) { /* !82489DX */
maxlvt = get_maxlvt();
if (maxlvt > 3) /* Due to the Pentium erratum 3AP. */
apic_write(APIC_ESR, 0);
value = apic_read(APIC_ESR);
printk("ESR value before enabling vector: %08lx\n", value);
value = ERROR_APIC_VECTOR; // enables sending errors
apic_write_around(APIC_LVTERR, value);
/*
* spec says clear errors after enabling vector.
*/
if (maxlvt > 3)
apic_write(APIC_ESR, 0);
value = apic_read(APIC_ESR);
printk("ESR value after enabling vector: %08lx\n", value);
} else {
printk("No ESR for 82489DX.\n");
}
}
static inline void apic_pm_init1(void) { }
static inline void apic_pm_init2(void) { }
/*
* Detect and enable local APICs on non-SMP boards.
* Original code written by Keir Fraser.
*/
static int __init detect_init_APIC (void)
{
u32 h, l, features;
extern void get_cpu_vendor(struct cpuinfo_x86*);
/* Workaround for us being called before identify_cpu(). */
get_cpu_vendor(&boot_cpu_data);
switch (boot_cpu_data.x86_vendor) {
case X86_VENDOR_AMD:
if (boot_cpu_data.x86 == 6 && boot_cpu_data.x86_model > 1)
break;
goto no_apic;
case X86_VENDOR_INTEL:
if (boot_cpu_data.x86 == 6 ||
(boot_cpu_data.x86 == 15 && cpu_has_apic) ||
(boot_cpu_data.x86 == 5 && cpu_has_apic))
break;
goto no_apic;
default:
goto no_apic;
}
if (!cpu_has_apic) {
/*
* Some BIOSes disable the local APIC in the
* APIC_BASE MSR. This can only be done in
* software for Intel P6 and AMD K7 (Model > 1).
*/
rdmsr(MSR_IA32_APICBASE, l, h);
if (!(l & MSR_IA32_APICBASE_ENABLE)) {
printk("Local APIC disabled by BIOS -- reenabling.\n");
l &= ~MSR_IA32_APICBASE_BASE;
l |= MSR_IA32_APICBASE_ENABLE | APIC_DEFAULT_PHYS_BASE;
wrmsr(MSR_IA32_APICBASE, l, h);
}
}
/*
* The APIC feature bit should now be enabled
* in `cpuid'
*/
features = cpuid_edx(1);
if (!(features & (1 << X86_FEATURE_APIC))) {
printk("Could not enable APIC!\n");
return -1;
}
set_bit(X86_FEATURE_APIC, &boot_cpu_data.x86_capability);
mp_lapic_addr = APIC_DEFAULT_PHYS_BASE;
boot_cpu_physical_apicid = 0;
printk("Found and enabled local APIC!\n");
apic_pm_init1();
return 0;
no_apic:
printk("No local APIC present or hardware disabled\n");
return -1;
}
void __init init_apic_mappings(void)
{
unsigned long apic_phys = 0;
/*
* If no local APIC can be found then set up a fake all zeroes page to
* simulate the local APIC and another one for the IO-APIC.
*/
if (!smp_found_config && detect_init_APIC()) {
apic_phys = get_free_page(GFP_KERNEL);
apic_phys = __pa(apic_phys);
} else
apic_phys = mp_lapic_addr;
set_fixmap_nocache(FIX_APIC_BASE, apic_phys);
Dprintk("mapped APIC to %08lx (%08lx)\n", APIC_BASE, apic_phys);
/*
* Fetch the APIC ID of the BSP in case we have a
* default configuration (or the MP table is broken).
*/
if (boot_cpu_physical_apicid == -1U)
boot_cpu_physical_apicid = GET_APIC_ID(apic_read(APIC_ID));
#ifdef CONFIG_X86_IO_APIC
{
unsigned long ioapic_phys = 0, idx = FIX_IO_APIC_BASE_0;
int i;
for (i = 0; i < nr_ioapics; i++) {
if (smp_found_config)
ioapic_phys = mp_ioapics[i].mpc_apicaddr;
set_fixmap_nocache(idx, ioapic_phys);
Dprintk("mapped IOAPIC to %08lx (%08lx)\n",
__fix_to_virt(idx), ioapic_phys);
idx++;
}
}
#endif
}
/*
* This part sets up the APIC 32 bit clock in LVTT1, with HZ interrupts
* per second. We assume that the caller has already set up the local
* APIC.
*
* The APIC timer is not exactly sync with the external timer chip, it
* closely follows bus clocks.
*/
/*
* The timer chip is already set up at HZ interrupts per second here,
* but we do not accept timer interrupts yet. We only allow the BP
* to calibrate.
*/
static unsigned int __init get_8254_timer_count(void)
{
/*extern spinlock_t i8253_lock;*/
/*unsigned long flags;*/
unsigned int count;
/*spin_lock_irqsave(&i8253_lock, flags);*/
outb_p(0x00, 0x43);
count = inb_p(0x40);
count |= inb_p(0x40) << 8;
/*spin_unlock_irqrestore(&i8253_lock, flags);*/
return count;
}
void __init wait_8254_wraparound(void)
{
unsigned int curr_count, prev_count=~0;
int delta;
curr_count = get_8254_timer_count();
do {
prev_count = curr_count;
curr_count = get_8254_timer_count();
delta = curr_count-prev_count;
/*
* This limit for delta seems arbitrary, but it isn't, it's
* slightly above the level of error a buggy Mercury/Neptune
* chipset timer can cause.
*/
} while (delta < 300);
}
/*
* This function sets up the local APIC timer, with a timeout of
* 'clocks' APIC bus clock. During calibration we actually call
* this function twice on the boot CPU, once with a bogus timeout
* value, second time for real. The other (noncalibrating) CPUs
* call this function only once, with the real, calibrated value.
*
* We do reads before writes even if unnecessary, to get around the
* P5 APIC double write bug.
*/
#define APIC_DIVISOR 16
void __setup_APIC_LVTT(unsigned int clocks)
{
unsigned int lvtt1_value, tmp_value;
lvtt1_value = SET_APIC_TIMER_BASE(APIC_TIMER_BASE_DIV) |
APIC_LVT_TIMER_PERIODIC | LOCAL_TIMER_VECTOR;
apic_write_around(APIC_LVTT, lvtt1_value);
/*
* Divide PICLK by 16
*/
tmp_value = apic_read(APIC_TDCR);
apic_write_around(APIC_TDCR, (tmp_value
& ~(APIC_TDR_DIV_1 | APIC_TDR_DIV_TMBASE))
| APIC_TDR_DIV_16);
apic_write_around(APIC_TMICT, clocks/APIC_DIVISOR);
}
void setup_APIC_timer(void * data)
{
unsigned int clocks = (unsigned int) data, slice, t0, t1;
unsigned long flags;
int delta;
__save_flags(flags);
__sti();
/*
* ok, Intel has some smart code in their APIC that knows
* if a CPU was in 'hlt' lowpower mode, and this increases
* its APIC arbitration priority. To avoid the external timer
* IRQ APIC event being in synchron with the APIC clock we
* introduce an interrupt skew to spread out timer events.
*
* The number of slices within a 'big' timeslice is smp_num_cpus+1
*/
slice = clocks / (smp_num_cpus+1);
printk("cpu: %d, clocks: %d, slice: %d\n",
smp_processor_id(), clocks, slice);
/*
* Wait for IRQ0's slice:
*/
wait_8254_wraparound();
__setup_APIC_LVTT(clocks);
t0 = apic_read(APIC_TMICT)*APIC_DIVISOR;
/* Wait till TMCCT gets reloaded from TMICT... */
do {
t1 = apic_read(APIC_TMCCT)*APIC_DIVISOR;
delta = (int)(t0 - t1 - slice*(smp_processor_id()+1));
} while (delta >= 0);
/* Now wait for our slice for real. */
do {
t1 = apic_read(APIC_TMCCT)*APIC_DIVISOR;
delta = (int)(t0 - t1 - slice*(smp_processor_id()+1));
} while (delta < 0);
__setup_APIC_LVTT(clocks);
printk("CPU%d<T0:%d,T1:%d,D:%d,S:%d,C:%d>\n",
smp_processor_id(), t0, t1, delta, slice, clocks);
__restore_flags(flags);
}
/*
* In this function we calibrate APIC bus clocks to the external timer.
*
* We want to do the calibration only once since we
* want to have local timer irqs syncron. CPUs connected
* by the same APIC bus have the very same bus frequency.
* And we want to have irqs off anyways, no accidental
* APIC irq that way.
*/
int __init calibrate_APIC_clock(void)
{
unsigned long long t1 = 0, t2 = 0;
long tt1, tt2;
long result;
int i;
const int LOOPS = HZ/10;
printk("calibrating APIC timer ...\n");
/*
* Put whatever arbitrary (but long enough) timeout
* value into the APIC clock, we just want to get the
* counter running for calibration.
*/
__setup_APIC_LVTT(1000000000);
/*
* The timer chip counts down to zero. Let's wait
* for a wraparound to start exact measurement:
* (the current tick might have been already half done)
*/
wait_8254_wraparound();
/*
* We wrapped around just now. Let's start:
*/
rdtscll(t1);
tt1 = apic_read(APIC_TMCCT);
/*
* Let's wait LOOPS wraprounds:
*/
for (i = 0; i < LOOPS; i++)
wait_8254_wraparound();
tt2 = apic_read(APIC_TMCCT);
rdtscll(t2);
/*
* The APIC bus clock counter is 32 bits only, it
* might have overflown, but note that we use signed
* longs, thus no extra care needed.
*
* underflown to be exact, as the timer counts down ;)
*/
result = (tt1-tt2)*APIC_DIVISOR/LOOPS;
printk("..... CPU clock speed is %ld.%04ld MHz.\n",
((long)(t2-t1)/LOOPS)/(1000000/HZ),
((long)(t2-t1)/LOOPS)%(1000000/HZ));
printk("..... host bus clock speed is %ld.%04ld MHz.\n",
result/(1000000/HZ),
result%(1000000/HZ));
return result;
}
static unsigned int calibration_result;
void __init setup_APIC_clocks (void)
{
printk("Using local APIC timer interrupts.\n");
using_apic_timer = 1;
__cli();
calibration_result = calibrate_APIC_clock();
/*
* Now set up the timer for real.
*/
setup_APIC_timer((void *)calibration_result);
__sti();
/* and update all other cpus */
smp_call_function(setup_APIC_timer, (void *)calibration_result, 1, 1);
}
#undef APIC_DIVISOR
/*
* Local timer interrupt handler. It does both profiling and
* process statistics/rescheduling.
*
* We do profiling in every local tick, statistics/rescheduling
* happen only every 'profiling multiplier' ticks. The default
* multiplier is 1 and it can be changed by writing the new multiplier
* value into /proc/profile.
*/
inline void smp_local_timer_interrupt(struct pt_regs * regs)
{
update_process_times(user_mode(regs));
}
/*
* Local APIC timer interrupt. This is the most natural way for doing
* local interrupts, but local timer interrupts can be emulated by
* broadcast interrupts too. [in case the hw doesnt support APIC timers]
*
* [ if a single-CPU system runs an SMP kernel then we call the local
* interrupt as well. Thus we cannot inline the local irq ... ]
*/
unsigned int apic_timer_irqs [NR_CPUS];
void smp_apic_timer_interrupt(struct pt_regs * regs)
{
int cpu = smp_processor_id();
/*
* the NMI deadlock-detector uses this.
*/
apic_timer_irqs[cpu]++;
/*
* NOTE! We'd better ACK the irq immediately,
* because timer handling can be slow.
*/
ack_APIC_irq();
/*
* update_process_times() expects us to have done irq_enter().
* Besides, if we don't timer interrupts ignore the global
* interrupt lock, which is the WrongThing (tm) to do.
*/
irq_enter(cpu, 0);
smp_local_timer_interrupt(regs);
irq_exit(cpu, 0);
if (softirq_pending(cpu))
do_softirq();
}
/*
* This interrupt should _never_ happen with our APIC/SMP architecture
*/
asmlinkage void smp_spurious_interrupt(void)
{
unsigned long v;
/*
* Check if this really is a spurious interrupt and ACK it
* if it is a vectored one. Just in case...
* Spurious interrupts should not be ACKed.
*/
v = apic_read(APIC_ISR + ((SPURIOUS_APIC_VECTOR & ~0x1f) >> 1));
if (v & (1 << (SPURIOUS_APIC_VECTOR & 0x1f)))
ack_APIC_irq();
/* see sw-dev-man vol 3, chapter 7.4.13.5 */
printk("spurious APIC interrupt on CPU#%d, should never happen.\n",
smp_processor_id());
}
/*
* This interrupt should never happen with our APIC/SMP architecture
*/
asmlinkage void smp_error_interrupt(void)
{
unsigned long v, v1;
/* First tickle the hardware, only then report what went on. -- REW */
v = apic_read(APIC_ESR);
apic_write(APIC_ESR, 0);
v1 = apic_read(APIC_ESR);
ack_APIC_irq();
atomic_inc(&irq_err_count);
/* Here is what the APIC error bits mean:
0: Send CS error
1: Receive CS error
2: Send accept error
3: Receive accept error
4: Reserved
5: Send illegal vector
6: Received illegal vector
7: Illegal register address
*/
printk ("APIC error on CPU%d: %02lx(%02lx)\n",
smp_processor_id(), v , v1);
}
/*
* This initializes the IO-APIC and APIC hardware if this is
* a UP kernel.
*/
int __init APIC_init_uniprocessor (void)
{
if (!smp_found_config && !cpu_has_apic)
return -1;
/*
* Complain if the BIOS pretends there is one.
*/
if (!cpu_has_apic && APIC_INTEGRATED(apic_version[boot_cpu_physical_apicid])) {
printk("BIOS bug, local APIC #%d not detected!...\n",
boot_cpu_physical_apicid);
return -1;
}
verify_local_APIC();
connect_bsp_APIC();
phys_cpu_present_map = 1;
apic_write_around(APIC_ID, boot_cpu_physical_apicid);
apic_pm_init2();
setup_local_APIC();
#ifdef CONFIG_X86_IO_APIC
if (smp_found_config && nr_ioapics)
setup_IO_APIC();
#endif
setup_APIC_clocks();
return 0;
}
+241
View File
@@ -0,0 +1,241 @@
#include <xeno/config.h>
#include <asm/page.h>
#define SECONDARY_CPU_FLAG 0xA5A5A5A5
.text
ENTRY(start)
jmp hal_entry
.align 4
/*** MULTIBOOT HEADER ****/
/* Magic number indicating a Multiboot header. */
.long 0x1BADB002
/* Flags to bootloader (see Multiboot spec). */
.long 0x00000006
/* Checksum: must be the negated sum of the first two fields. */
.long -0x1BADB008
/* Unused loader addresses (ELF header has all this already).*/
.long 0,0,0,0,0
/* EGA text mode. */
.long 1,0,0,0
hal_entry:
/* Set up a few descriptors: on entry only CS is guaranteed good. */
lgdt %cs:nopaging_gdt_descr-__PAGE_OFFSET
mov $(__HYPERVISOR_DS),%ecx
mov %ecx,%ds
mov %ecx,%es
ljmp $(__HYPERVISOR_CS),$(1f)-__PAGE_OFFSET
1: lss stack_start-__PAGE_OFFSET,%esp
/* Reset EFLAGS (subsumes CLI and CLD). */
pushl $0
popf
/* CPU type checks. We need P6+. */
mov $0x200000,%edx
pushfl
pop %ecx
and %edx,%ecx
jne bad_cpu # ID bit should be clear
pushl %edx
popfl
pushfl
pop %ecx
and %edx,%ecx
je bad_cpu # ID bit should be set
/* Set up CR0. */
mov %cr0,%ecx
and $0x00000011,%ecx # save ET and PE
or $0x00050022,%ecx # set AM, WP, NE and MP
mov %ecx,%cr0
/* Set up FPU. */
fninit
/* Set up CR4, except global flag which Intel requires should be */
/* left until after paging is enabled (IA32 Manual Vol. 3, Sec. 2.5) */
mov %cr4,%ecx
or mmu_cr4_features-__PAGE_OFFSET,%ecx
mov %ecx,mmu_cr4_features-__PAGE_OFFSET
and $0x7f,%ecx /* disable GLOBAL bit */
mov %ecx,%cr4
/* Is this a non-boot processor? */
cmp $(SECONDARY_CPU_FLAG),%ebx
jne continue_boot_cpu
call start_paging
lidt idt_descr
jmp initialize_secondary
continue_boot_cpu:
add $__PAGE_OFFSET,%ebx
push %ebx /* Multiboot info struct */
push %eax /* Multiboot magic value */
/* Initialize BSS (no nasty surprises!) */
mov $__bss_start-__PAGE_OFFSET,%edi
mov $_end-__PAGE_OFFSET,%ecx
sub %edi,%ecx
xor %eax,%eax
rep stosb
/* Initialize low and high mappings of all memory with 4MB pages */
mov $idle0_pg_table-__PAGE_OFFSET,%edi
mov $0x1e3,%eax /* PRESENT+RW+A+D+4MB+GLOBAL */
1: mov %eax,__PAGE_OFFSET>>20(%edi) /* high mapping */
stosl /* low mapping */
add $(1<<L2_PAGETABLE_SHIFT),%eax
cmp $MAX_USABLE_ADDRESS+0x1e3,%eax
jne 1b
call start_paging
call setup_idt
lidt idt_descr
/* Call into main C routine. This should never return.*/
call cmain
ud2 /* Force a panic (invalid opcode). */
start_paging:
mov $idle0_pg_table-__PAGE_OFFSET,%eax
mov %eax,%cr3
mov %cr0,%eax
or $0x80010000,%eax /* set PG and WP bits */
mov %eax,%cr0
jmp 1f
1: /* Install relocated selectors (FS/GS unused). */
lgdt gdt_descr
mov $(__HYPERVISOR_DS),%ecx
mov %ecx,%ds
mov %ecx,%es
mov %ecx,%ss
ljmp $(__HYPERVISOR_CS),$1f
1: /* Paging enabled, so we can now enable GLOBAL mappings in CR4. */
movl mmu_cr4_features,%ecx
movl %ecx,%cr4
/* Relocate ESP */
add $__PAGE_OFFSET,%esp
/* Relocate EIP via return jump */
pop %ecx
add $__PAGE_OFFSET,%ecx
jmp *%ecx
/*** INTERRUPT INITIALISATION ***/
setup_idt:
lea ignore_int,%edx
mov $(__HYPERVISOR_CS << 16),%eax
mov %dx,%ax /* selector = 0x0010 = cs */
mov $0x8E00,%dx /* interrupt gate - dpl=0, present */
lea SYMBOL_NAME(idt_table),%edi
mov $256,%ecx
1: mov %eax,(%edi)
mov %edx,4(%edi)
add $8,%edi
loop 1b
ret
/* This is the default interrupt handler. */
int_msg:
.asciz "Unknown interrupt\n"
ALIGN
ignore_int:
cld
push %eax
push %ecx
push %edx
pushl %es
pushl %ds
mov $(__HYPERVISOR_DS),%eax
mov %eax,%ds
mov %eax,%es
pushl $int_msg
call SYMBOL_NAME(printf)
1: jmp 1b
pop %eax
popl %ds
popl %es
pop %edx
pop %ecx
pop %eax
iret
bad_cpu_msg:
.asciz "Bad CPU type. Need P6+."
ALIGN
bad_cpu:
call init_serial
mov $bad_cpu_msg,%esi
1: lodsb
test %al,%al
je 1f
push %eax
call putchar_serial
add $4,%esp
jmp 1b
1: jmp 1b
/*** STACK LOCATION ***/
ENTRY(stack_start)
.long SYMBOL_NAME(idle0_task_union)+8192-__PAGE_OFFSET
.long __HYPERVISOR_DS
/*** DESCRIPTOR TABLES ***/
.globl SYMBOL_NAME(idt)
.globl SYMBOL_NAME(gdt)
ALIGN
.word 0
idt_descr:
.word 256*8-1
SYMBOL_NAME(idt):
.long SYMBOL_NAME(idt_table)
.word 0
gdt_descr:
.word 256*8-1
SYMBOL_NAME(gdt):
.long SYMBOL_NAME(gdt_table) /* gdt base */
.word 0
nopaging_gdt_descr:
.word 256*8-1
.long SYMBOL_NAME(gdt_table)-__PAGE_OFFSET
ALIGN
ENTRY(gdt_table)
.quad 0x0000000000000000 /* NULL descriptor */
.quad 0x0000000000000000 /* not used */
.quad 0x00ceba000000ffff /* 0x11 ring 1 3.5GB code at 0x00000000 */
.quad 0x00ceb2000000ffff /* 0x19 ring 1 3.5GB data at 0x00000000 */
.quad 0x00cefa000000ffff /* 0x23 ring 3 3.5GB code at 0x00000000 */
.quad 0x00cef2000000ffff /* 0x2b ring 3 3.5GB data at 0x00000000 */
.quad 0x00cf9a000000ffff /* 0x30 ring 0 4.0GB code at 0x00000000 */
.quad 0x00cf92000000ffff /* 0x38 ring 0 4.0GB data at 0x00000000 */
.quad 0x0000000000000000
.quad 0x0000000000000000
.quad 0x0000000000000000
.quad 0x0000000000000000
.fill NR_CPUS*4,8,0 /* space for TSS's and LDT's */
# The following adds 12kB to the kernel file size.
.org 0x1000
ENTRY(idle0_pg_table)
.org 0x2000
ENTRY(idle0_task_union)
.org 0x4000
ENTRY(stext)
ENTRY(_stext)
+29
View File
@@ -0,0 +1,29 @@
/*
* Precise Delay Loops for i386
*
* Copyright (C) 1993 Linus Torvalds
* Copyright (C) 1997 Martin Mares <mj@atrey.karlin.mff.cuni.cz>
*
* The __delay function must _NOT_ be inlined as its execution time
* depends wildly on alignment on many x86 processors. The additional
* jump magic is needed to get the timing stable on all the CPU's
* we have to worry about.
*/
#include <xeno/config.h>
#include <xeno/delay.h>
#include <asm/msr.h>
#include <asm/processor.h>
void __udelay(unsigned long usecs)
{
unsigned long ticks = usecs * ticks_per_usec;
unsigned long s, e;
rdtscl(s);
do
{
rep_nop();
rdtscl(e);
} while ((e-s) < ticks);
}
+526
View File
@@ -0,0 +1,526 @@
/*
* linux/arch/i386/entry.S
*
* Copyright (C) 1991, 1992 Linus Torvalds
*/
/*
* entry.S contains the system-call and fault low-level handling routines.
* This also contains the timer-interrupt handler, as well as all interrupts
* and faults that can result in a task-switch.
*
* Stack layout in 'ret_from_system_call':
* 0(%esp) - %ebx
* 4(%esp) - %ecx
* 8(%esp) - %edx
* C(%esp) - %esi
* 10(%esp) - %edi
* 14(%esp) - %ebp
* 18(%esp) - %eax
* 1C(%esp) - %ds
* 20(%esp) - %es
* 24(%esp) - orig_eax
* 28(%esp) - %eip
* 2C(%esp) - %cs
* 30(%esp) - %eflags
* 34(%esp) - %oldesp
* 38(%esp) - %oldss
*
* "current" is in register %ebx during any slow entries.
*/
/* The idea for callbacks from monitor -> guest OS.
*
* First, we require that all callbacks (either via a supplied
* interrupt-descriptor-table, or via the special event or failsafe callbacks
* in the shared-info-structure) are to ring 1. This just makes life easier,
* in that it means we don't have to do messy GDT/LDT lookups to find
* out which the privilege-level of the return code-selector. That code
* would just be a hassle to write, and would need to account for running
* off the end of the GDT/LDT, for example. The event callback has quite
* a constrained callback method: the guest OS provides a linear address
* which we call back to using the hard-coded __GUEST_CS descriptor (which
* is a ring 1 descriptor). For IDT callbacks, we check that the provided
* return CS is not == __HYPERVISOR_{CS,DS}. Apart from that we're safe as
* don't allow a guest OS to install ring-0 privileges into the GDT/LDT.
* It's up to the guest OS to ensure all returns via the IDT are to ring 1.
* If not, we load incorrect SS/ESP values from the TSS (for ring 1 rather
* than the correct ring) and bad things are bound to ensue -- IRET is
* likely to fault, and we may end up killing the domain (no harm can
* come to the hypervisor itself, though).
*
* When doing a callback, we check if the return CS is in ring 0. If so,
* callback is delayed until next return to ring != 0.
* If return CS is in ring 1, then we create a callback frame
* starting at return SS/ESP. The base of the frame does an intra-privilege
* interrupt-return.
* If return CS is in ring > 1, we create a callback frame starting
* at SS/ESP taken from appropriate section of the current TSS. The base
* of the frame does an inter-privilege interrupt-return.
*
* Note that the "failsafe callback" uses a special stackframe:
* { return_DS, return_ES, return_EIP, return_CS, return_EFLAGS, ... }
* That is, original values for DS/ES are placed on stack rather than
* in DS/ES themselves. Why? It saves us loading them, only to have them
* saved/restored in guest OS. Furthermore, if we load them we may cause
* a fault if they are invalid, which is a hassle to deal with. We avoid
* that problem if we don't load them :-) This property allows us to use
* the failsafe callback as a fallback: if we ever fault on loading DS/ES
* on return to ring != 0, we can simply package it up as a return via
* the failsafe callback, and let the guest OS sort it out (perhaps by
* killing an application process). Note that we also do this for any
* faulting IRET -- just let the guest OS handle it via the event
* callback.
*
* We terminate a domain in the following cases:
* - creating a callback stack frame (due to bad ring-1 stack).
* - faulting IRET on entry to failsafe callback handler.
* So, each domain must keep its ring-1 %ss/%esp and failsafe callback
* handler in good order (absolutely no faults allowed!).
*/
#include <xeno/config.h>
#include <asm/smp.h>
EBX = 0x00
ECX = 0x04
EDX = 0x08
ESI = 0x0C
EDI = 0x10
EBP = 0x14
EAX = 0x18
DS = 0x1C
ES = 0x20
ORIG_EAX = 0x24
EIP = 0x28
CS = 0x2C
EFLAGS = 0x30
OLDESP = 0x34
OLDSS = 0x38
/* Offsets in task_struct */
PROCESSOR = 0
STATE = 4
HYP_EVENTS = 8
DOMAIN = 12
SHARED_INFO = 16
/* Offsets in shared_info_t */
EVENTS = 0
EVENTS_ENABLE = 4
EVENT_ADDR = 8
FAILSAFE_ADDR = 12
/* Offsets in guest_trap_bounce */
GTB_ERROR_CODE = 0
GTB_CR2 = 4
GTB_FLAGS = 8
GTB_CS = 10
GTB_EIP = 12
GTBF_TRAP = 1
GTBF_TRAP_NOCODE = 2
GTBF_TRAP_CR2 = 4
CF_MASK = 0x00000001
IF_MASK = 0x00000200
NT_MASK = 0x00004000
#define SAVE_ALL \
cld; \
pushl %es; \
pushl %ds; \
pushl %eax; \
pushl %ebp; \
pushl %edi; \
pushl %esi; \
pushl %edx; \
pushl %ecx; \
pushl %ebx; \
movl $(__HYPERVISOR_DS),%edx; \
movl %edx,%ds; \
movl %edx,%es;
#define RESTORE_ALL \
popl %ebx; \
popl %ecx; \
popl %edx; \
popl %esi; \
popl %edi; \
popl %ebp; \
popl %eax; \
1: popl %ds; \
2: popl %es; \
addl $4,%esp; \
3: iret; \
.section .fixup,"ax"; \
6: subl $4,%esp; \
pushl %es; \
5: pushl %ds; \
4: pushl %eax; \
pushl %ebp; \
pushl %edi; \
pushl %esi; \
pushl %edx; \
pushl %ecx; \
pushl %ebx; \
pushl %ss; \
popl %ds; \
pushl %ss; \
popl %es; \
jmp failsafe_callback; \
.previous; \
.section __ex_table,"a"; \
.align 4; \
.long 1b,4b; \
.long 2b,5b; \
.long 3b,6b; \
.previous
#define GET_CURRENT(reg) \
movl $-8192, reg; \
andl %esp, reg
ENTRY(ret_from_newdomain)
GET_CURRENT(%ebx)
jmp test_all_events
ALIGN
restore_all:
RESTORE_ALL
ALIGN
ENTRY(hypervisor_call)
pushl %eax # save orig_eax
SAVE_ALL
GET_CURRENT(%ebx)
andl $255,%eax
call *SYMBOL_NAME(hypervisor_call_table)(,%eax,4)
movl %eax,EAX(%esp) # save the return value
test_all_events:
mov PROCESSOR(%ebx),%eax
shl $4,%eax # sizeof(irq_cpustat) == 16
lea guest_trap_bounce(%eax),%edx
cli # tests must not race interrupts
xorl %ecx,%ecx
notl %ecx
test_softirqs:
mov PROCESSOR(%ebx),%eax
shl $4,%eax # sizeof(irq_cpustat) == 16
test %ecx,SYMBOL_NAME(irq_stat)(%eax,1)
jnz process_softirqs
test_hyp_events:
test %ecx, HYP_EVENTS(%ebx)
jnz process_hyp_events
test_guest_events:
movl SHARED_INFO(%ebx),%eax
test %ecx,EVENTS(%eax)
jz restore_all
test %ecx,EVENTS_ENABLE(%eax)
jz restore_all
/* Prevent unnecessary reentry of event callback (stack overflow!) */
xorl %ecx,%ecx
movl %ecx,EVENTS_ENABLE(%eax)
/* %eax == shared_info, %ebx == task_struct, %edx == guest_trap_bounce */
process_guest_events:
movl EVENT_ADDR(%eax),%eax
movl %eax,GTB_EIP(%edx)
movw $__GUEST_CS,GTB_CS(%edx)
call create_bounce_frame
jmp restore_all
ALIGN
process_softirqs:
push %edx
call SYMBOL_NAME(do_softirq)
pop %edx
jmp test_hyp_events
ALIGN
process_hyp_events:
sti
call SYMBOL_NAME(do_hyp_events)
jmp test_all_events
/* No special register assumptions */
failsafe_callback:
GET_CURRENT(%ebx)
mov PROCESSOR(%ebx),%eax
shl $4,%eax
lea guest_trap_bounce(%eax),%edx
movl SHARED_INFO(%ebx),%eax
movl FAILSAFE_ADDR(%eax),%eax
movl %eax,GTB_EIP(%edx)
movw $__GUEST_CS,GTB_CS(%edx)
call create_bounce_frame
subl $8,%esi # add DS/ES to failsafe stack frame
movl DS(%esp),%eax
FAULT3: movl %eax,(%esi)
movl ES(%esp),%eax
FAULT4: movl %eax,4(%esi)
movl %esi,OLDESP(%esp)
popl %ebx
popl %ecx
popl %edx
popl %esi
popl %edi
popl %ebp
popl %eax
addl $12,%esp
FAULT5: iret
/* CREATE A BASIC EXCEPTION FRAME ON GUEST OS (RING-1) STACK: */
/* {EIP, CS, EFLAGS, [ESP, SS]} */
/* %edx == guest_trap_bounce, %ebx == task_struct */
/* %eax,%ecx are clobbered. %ds:%esi contain new OLDSS/OLDESP. */
create_bounce_frame:
mov CS+4(%esp),%cl
test $2,%cl
jz 1f /* jump if returning to an existing ring-1 activation */
/* obtain ss/esp from TSS -- no current ring-1 activations */
movl PROCESSOR(%ebx),%eax
shll $8,%eax /* multiply by 256 */
addl $init_tss + 12,%eax
movl (%eax),%esi /* tss->esp1 */
FAULT6: movl 4(%eax),%ds /* tss->ss1 */
/* base of stack frame must contain ss/esp (inter-priv iret) */
subl $8,%esi
movl OLDESP+4(%esp),%eax
FAULT7: movl %eax,(%esi)
movl OLDSS+4(%esp),%eax
FAULT8: movl %eax,4(%esi)
jmp 2f
1: /* obtain ss/esp from oldss/oldesp -- a ring-1 activation exists */
movl OLDESP+4(%esp),%esi
FAULT9: movl OLDSS+4(%esp),%ds
2: /* Construct a stack frame: EFLAGS, CS/EIP */
subl $12,%esi
movl EIP+4(%esp),%eax
FAULT10:movl %eax,(%esi)
movl CS+4(%esp),%eax
FAULT11:movl %eax,4(%esi)
movl EFLAGS+4(%esp),%eax
FAULT12:movl %eax,8(%esi)
/* Rewrite our stack frame and return to ring 1. */
movl %ds,OLDSS+4(%esp)
movl %esi,OLDESP+4(%esp)
movzwl %es:GTB_CS(%edx),%eax
movl %eax,CS+4(%esp)
movl %es:GTB_EIP(%edx),%eax
movl %eax,EIP+4(%esp)
ret
.section __ex_table,"a"
.align 4
.long FAULT1, kill_domain_fixup3 # Fault writing to ring-1 stack
.long FAULT2, kill_domain_fixup3 # Fault writing to ring-1 stack
.long FAULT3, kill_domain_fixup3 # Fault writing to ring-1 stack
.long FAULT4, kill_domain_fixup3 # Fault writing to ring-1 stack
.long FAULT5, kill_domain_fixup1 # Fault executing failsafe iret
.long FAULT6, kill_domain_fixup2 # Fault loading ring-1 stack selector
.long FAULT7, kill_domain_fixup2 # Fault writing to ring-1 stack
.long FAULT8, kill_domain_fixup2 # Fault writing to ring-1 stack
.long FAULT9, kill_domain_fixup2 # Fault loading ring-1 stack selector
.long FAULT10,kill_domain_fixup2 # Fault writing to ring-1 stack
.long FAULT11,kill_domain_fixup2 # Fault writing to ring-1 stack
.long FAULT12,kill_domain_fixup2 # Fault writing to ring-1 stack
.previous
# This handler kills domains which experience unrecoverable faults.
.section .fixup,"ax"
kill_domain_fixup1:
subl $4,%esp
SAVE_ALL
jmp kill_domain
kill_domain_fixup2:
addl $4,%esp
kill_domain_fixup3:
pushl %ss
popl %ds
jmp kill_domain
.previous
ALIGN
process_guest_exception_and_events:
mov PROCESSOR(%ebx),%eax
shl $4,%eax # sizeof(irq_cpustat) == 16
lea guest_trap_bounce(%eax),%edx
testb $~0,GTB_FLAGS(%edx)
jz test_all_events
call create_bounce_frame # just the basic frame
mov %es:GTB_FLAGS(%edx),%cl
test $GTBF_TRAP_NOCODE,%cl
jnz 2f
subl $4,%esi # push error_code onto guest frame
movl %es:GTB_ERROR_CODE(%edx),%eax
FAULT1: movl %eax,(%esi)
test $GTBF_TRAP_CR2,%cl
jz 1f
subl $4,%esi # push %cr2 onto guest frame
movl %es:GTB_CR2(%edx),%eax
FAULT2: movl %eax,(%esi)
1: movl %esi,OLDESP(%esp)
2: push %es # unclobber %ds
pop %ds
movb $0,GTB_FLAGS(%edx)
jmp test_all_events
ALIGN
ENTRY(ret_from_intr)
GET_CURRENT(%ebx)
movb CS(%esp),%al
testb $3,%al # return to non-supervisor?
jne test_all_events
jmp restore_all
ALIGN
ret_from_exception:
movb CS(%esp),%al
testb $3,%al # return to non-supervisor?
jne process_guest_exception_and_events
jmp restore_all
ALIGN
ENTRY(divide_error)
pushl $0 # no error code
pushl $ SYMBOL_NAME(do_divide_error)
ALIGN
error_code:
pushl %ds
pushl %eax
xorl %eax,%eax
pushl %ebp
pushl %edi
pushl %esi
pushl %edx
decl %eax # eax = -1
pushl %ecx
pushl %ebx
cld
movl %es,%ecx
movl ORIG_EAX(%esp), %esi # get the error code
movl ES(%esp), %edi # get the function address
movl %eax, ORIG_EAX(%esp)
movl %ecx, ES(%esp)
movl %esp,%edx
pushl %esi # push the error code
pushl %edx # push the pt_regs pointer
movl $(__HYPERVISOR_DS),%edx
movl %edx,%ds
movl %edx,%es
GET_CURRENT(%ebx)
call *%edi
addl $8,%esp
jmp ret_from_exception
ENTRY(coprocessor_error)
pushl $0
pushl $ SYMBOL_NAME(do_coprocessor_error)
jmp error_code
ENTRY(simd_coprocessor_error)
pushl $0
pushl $ SYMBOL_NAME(do_simd_coprocessor_error)
jmp error_code
ENTRY(device_not_available)
pushl $0
pushl $SYMBOL_NAME(math_state_restore)
jmp error_code
ENTRY(debug)
pushl $0
pushl $ SYMBOL_NAME(do_debug)
jmp error_code
ENTRY(nmi)
pushl %eax
SAVE_ALL
movl %esp,%edx
pushl $0
pushl %edx
call SYMBOL_NAME(do_nmi)
addl $8,%esp
RESTORE_ALL
ENTRY(int3)
pushl $0
pushl $ SYMBOL_NAME(do_int3)
jmp error_code
ENTRY(overflow)
pushl $0
pushl $ SYMBOL_NAME(do_overflow)
jmp error_code
ENTRY(bounds)
pushl $0
pushl $ SYMBOL_NAME(do_bounds)
jmp error_code
ENTRY(invalid_op)
pushl $0
pushl $ SYMBOL_NAME(do_invalid_op)
jmp error_code
ENTRY(coprocessor_segment_overrun)
pushl $0
pushl $ SYMBOL_NAME(do_coprocessor_segment_overrun)
jmp error_code
ENTRY(double_fault)
pushl $ SYMBOL_NAME(do_double_fault)
jmp error_code
ENTRY(invalid_TSS)
pushl $ SYMBOL_NAME(do_invalid_TSS)
jmp error_code
ENTRY(segment_not_present)
pushl $ SYMBOL_NAME(do_segment_not_present)
jmp error_code
ENTRY(stack_segment)
pushl $ SYMBOL_NAME(do_stack_segment)
jmp error_code
ENTRY(general_protection)
pushl $ SYMBOL_NAME(do_general_protection)
jmp error_code
ENTRY(alignment_check)
pushl $ SYMBOL_NAME(do_alignment_check)
jmp error_code
ENTRY(page_fault)
pushl $ SYMBOL_NAME(do_page_fault)
jmp error_code
ENTRY(machine_check)
pushl $0
pushl $ SYMBOL_NAME(do_machine_check)
jmp error_code
ENTRY(spurious_interrupt_bug)
pushl $0
pushl $ SYMBOL_NAME(do_spurious_interrupt_bug)
jmp error_code
.data
ENTRY(hypervisor_call_table)
.long SYMBOL_NAME(do_set_trap_table)
.long SYMBOL_NAME(do_process_page_updates)
.long SYMBOL_NAME(do_console_write)
.long SYMBOL_NAME(do_set_pagetable)
.long SYMBOL_NAME(do_set_guest_stack)
.long SYMBOL_NAME(do_net_update)
.long SYMBOL_NAME(do_fpu_taskswitch)
.long SYMBOL_NAME(do_yield)
.long SYMBOL_NAME(kill_domain)
.long SYMBOL_NAME(do_dom0_op)
.long SYMBOL_NAME(do_network_op)
.rept NR_syscalls-(.-hypervisor_call_table)/4
.long SYMBOL_NAME(sys_ni_syscall)
.endr
+62
View File
@@ -0,0 +1,62 @@
/*
* linux/arch/i386/mm/extable.c
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/spinlock.h>
#include <asm/uaccess.h>
extern const struct exception_table_entry __start___ex_table[];
extern const struct exception_table_entry __stop___ex_table[];
static inline unsigned long
search_one_table(const struct exception_table_entry *first,
const struct exception_table_entry *last,
unsigned long value)
{
while (first <= last) {
const struct exception_table_entry *mid;
long diff;
mid = (last - first) / 2 + first;
diff = mid->insn - value;
if (diff == 0)
return mid->fixup;
else if (diff < 0)
first = mid+1;
else
last = mid-1;
}
return 0;
}
extern spinlock_t modlist_lock;
unsigned long
search_exception_table(unsigned long addr)
{
unsigned long ret = 0;
#ifndef CONFIG_MODULES
/* There is only the kernel to search. */
ret = search_one_table(__start___ex_table, __stop___ex_table-1, addr);
return ret;
#else
unsigned long flags;
/* The kernel is the last "module" -- no need to treat it special. */
struct module *mp;
spin_lock_irqsave(&modlist_lock, flags);
for (mp = module_list; mp != NULL; mp = mp->next) {
if (mp->ex_table_start == NULL || !(mp->flags&(MOD_RUNNING|MOD_INITIALIZING)))
continue;
ret = search_one_table(mp->ex_table_start,
mp->ex_table_end - 1, addr);
if (ret)
break;
}
spin_unlock_irqrestore(&modlist_lock, flags);
return ret;
#endif
}
+50
View File
@@ -0,0 +1,50 @@
/*
* linux/arch/i386/kernel/i387.c
*
* Copyright (C) 1994 Linus Torvalds
*
* Pentium III FXSR, SSE support
* General FPU state handling cleanups
* Gareth Hughes <gareth@valinux.com>, May 2000
*/
#include <xeno/config.h>
#include <xeno/sched.h>
#include <asm/processor.h>
#include <asm/i387.h>
void init_fpu(void)
{
__asm__("fninit");
if ( cpu_has_xmm ) load_mxcsr(0x1f80);
current->flags |= PF_DONEFPUINIT;
}
static inline void __save_init_fpu( struct task_struct *tsk )
{
if ( cpu_has_fxsr ) {
asm volatile( "fxsave %0 ; fnclex"
: "=m" (tsk->thread.i387.fxsave) );
} else {
asm volatile( "fnsave %0 ; fwait"
: "=m" (tsk->thread.i387.fsave) );
}
tsk->flags &= ~PF_USEDFPU;
}
void save_init_fpu( struct task_struct *tsk )
{
__save_init_fpu(tsk);
stts();
}
void restore_fpu( struct task_struct *tsk )
{
if ( cpu_has_fxsr ) {
asm volatile( "fxrstor %0"
: : "m" (tsk->thread.i387.fxsave) );
} else {
asm volatile( "frstor %0"
: : "m" (tsk->thread.i387.fsave) );
}
}
+469
View File
@@ -0,0 +1,469 @@
/******************************************************************************
* i8259.c
*
* Well, this is required for SMP systems as well, as it build interrupt
* tables for IO APICS as well as uniprocessor 8259-alikes.
*/
#include <xeno/config.h>
#include <xeno/init.h>
#include <asm/ptrace.h>
#include <xeno/errno.h>
#include <xeno/sched.h>
#include <xeno/interrupt.h>
#include <xeno/irq.h>
#include <asm/atomic.h>
#include <asm/system.h>
#include <asm/io.h>
#include <asm/desc.h>
#include <asm/bitops.h>
#include <xeno/delay.h>
#include <asm/apic.h>
/*
* Common place to define all x86 IRQ vectors
*
* This builds up the IRQ handler stubs using some ugly macros in irq.h
*
* These macros create the low-level assembly IRQ routines that save
* register context and call do_IRQ(). do_IRQ() then does all the
* operations that are needed to keep the AT (or SMP IOAPIC)
* interrupt-controller happy.
*/
BUILD_COMMON_IRQ()
#define BI(x,y) \
BUILD_IRQ(x##y)
#define BUILD_16_IRQS(x) \
BI(x,0) BI(x,1) BI(x,2) BI(x,3) \
BI(x,4) BI(x,5) BI(x,6) BI(x,7) \
BI(x,8) BI(x,9) BI(x,a) BI(x,b) \
BI(x,c) BI(x,d) BI(x,e) BI(x,f)
/*
* ISA PIC or low IO-APIC triggered (INTA-cycle or APIC) interrupts:
* (these are usually mapped to vectors 0x20-0x2f)
*/
BUILD_16_IRQS(0x0)
#ifdef CONFIG_X86_IO_APIC
/*
* The IO-APIC gives us many more interrupt sources. Most of these
* are unused but an SMP system is supposed to have enough memory ...
* sometimes (mostly wrt. hw bugs) we get corrupted vectors all
* across the spectrum, so we really want to be prepared to get all
* of these. Plus, more powerful systems might have more than 64
* IO-APIC registers.
*
* (these are usually mapped into the 0x30-0xff vector range)
*/
BUILD_16_IRQS(0x1) BUILD_16_IRQS(0x2) BUILD_16_IRQS(0x3)
BUILD_16_IRQS(0x4) BUILD_16_IRQS(0x5) BUILD_16_IRQS(0x6) BUILD_16_IRQS(0x7)
BUILD_16_IRQS(0x8) BUILD_16_IRQS(0x9) BUILD_16_IRQS(0xa) BUILD_16_IRQS(0xb)
BUILD_16_IRQS(0xc) BUILD_16_IRQS(0xd)
#endif
#undef BUILD_16_IRQS
#undef BI
/*
* The following vectors are part of the Linux architecture, there
* is no hardware IRQ pin equivalent for them, they are triggered
* through the ICC by us (IPIs)
*/
#ifdef CONFIG_SMP
BUILD_SMP_INTERRUPT(event_check_interrupt,EVENT_CHECK_VECTOR)
BUILD_SMP_INTERRUPT(invalidate_interrupt,INVALIDATE_TLB_VECTOR)
BUILD_SMP_INTERRUPT(call_function_interrupt,CALL_FUNCTION_VECTOR)
#endif
/*
* every pentium local APIC has two 'local interrupts', with a
* soft-definable vector attached to both interrupts, one of
* which is a timer interrupt, the other one is error counter
* overflow. Linux uses the local APIC timer interrupt to get
* a much simpler SMP time architecture:
*/
#ifdef CONFIG_X86_LOCAL_APIC
BUILD_SMP_TIMER_INTERRUPT(apic_timer_interrupt,LOCAL_TIMER_VECTOR)
BUILD_SMP_INTERRUPT(error_interrupt,ERROR_APIC_VECTOR)
BUILD_SMP_INTERRUPT(spurious_interrupt,SPURIOUS_APIC_VECTOR)
#endif
#define IRQ(x,y) \
IRQ##x##y##_interrupt
#define IRQLIST_16(x) \
IRQ(x,0), IRQ(x,1), IRQ(x,2), IRQ(x,3), \
IRQ(x,4), IRQ(x,5), IRQ(x,6), IRQ(x,7), \
IRQ(x,8), IRQ(x,9), IRQ(x,a), IRQ(x,b), \
IRQ(x,c), IRQ(x,d), IRQ(x,e), IRQ(x,f)
void (*interrupt[NR_IRQS])(void) = {
IRQLIST_16(0x0),
#ifdef CONFIG_X86_IO_APIC
IRQLIST_16(0x1), IRQLIST_16(0x2), IRQLIST_16(0x3),
IRQLIST_16(0x4), IRQLIST_16(0x5), IRQLIST_16(0x6), IRQLIST_16(0x7),
IRQLIST_16(0x8), IRQLIST_16(0x9), IRQLIST_16(0xa), IRQLIST_16(0xb),
IRQLIST_16(0xc), IRQLIST_16(0xd)
#endif
};
#undef IRQ
#undef IRQLIST_16
/*
* This is the 'legacy' 8259A Programmable Interrupt Controller,
* present in the majority of PC/AT boxes.
* plus some generic x86 specific things if generic specifics makes
* any sense at all.
* this file should become arch/i386/kernel/irq.c when the old irq.c
* moves to arch independent land
*/
spinlock_t i8259A_lock = SPIN_LOCK_UNLOCKED;
static void end_8259A_irq (unsigned int irq)
{
if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS)))
enable_8259A_irq(irq);
}
#define shutdown_8259A_irq disable_8259A_irq
void mask_and_ack_8259A(unsigned int);
static unsigned int startup_8259A_irq(unsigned int irq)
{
enable_8259A_irq(irq);
return 0; /* never anything pending */
}
static struct hw_interrupt_type i8259A_irq_type = {
"XT-PIC",
startup_8259A_irq,
shutdown_8259A_irq,
enable_8259A_irq,
disable_8259A_irq,
mask_and_ack_8259A,
end_8259A_irq,
NULL
};
/*
* 8259A PIC functions to handle ISA devices:
*/
/*
* This contains the irq mask for both 8259A irq controllers,
*/
static unsigned int cached_irq_mask = 0xffff;
#define __byte(x,y) (((unsigned char *)&(y))[x])
#define cached_21 (__byte(0,cached_irq_mask))
#define cached_A1 (__byte(1,cached_irq_mask))
/*
* Not all IRQs can be routed through the IO-APIC, eg. on certain (older)
* boards the timer interrupt is not really connected to any IO-APIC pin,
* it's fed to the master 8259A's IR0 line only.
*
* Any '1' bit in this mask means the IRQ is routed through the IO-APIC.
* this 'mixed mode' IRQ handling costs nothing because it's only used
* at IRQ setup time.
*/
unsigned long io_apic_irqs;
void disable_8259A_irq(unsigned int irq)
{
unsigned int mask = 1 << irq;
unsigned long flags;
spin_lock_irqsave(&i8259A_lock, flags);
cached_irq_mask |= mask;
if (irq & 8)
outb(cached_A1,0xA1);
else
outb(cached_21,0x21);
spin_unlock_irqrestore(&i8259A_lock, flags);
}
void enable_8259A_irq(unsigned int irq)
{
unsigned int mask = ~(1 << irq);
unsigned long flags;
spin_lock_irqsave(&i8259A_lock, flags);
cached_irq_mask &= mask;
if (irq & 8)
outb(cached_A1,0xA1);
else
outb(cached_21,0x21);
spin_unlock_irqrestore(&i8259A_lock, flags);
}
int i8259A_irq_pending(unsigned int irq)
{
unsigned int mask = 1<<irq;
unsigned long flags;
int ret;
spin_lock_irqsave(&i8259A_lock, flags);
if (irq < 8)
ret = inb(0x20) & mask;
else
ret = inb(0xA0) & (mask >> 8);
spin_unlock_irqrestore(&i8259A_lock, flags);
return ret;
}
void make_8259A_irq(unsigned int irq)
{
disable_irq_nosync(irq);
io_apic_irqs &= ~(1<<irq);
irq_desc[irq].handler = &i8259A_irq_type;
enable_irq(irq);
}
/*
* This function assumes to be called rarely. Switching between
* 8259A registers is slow.
* This has to be protected by the irq controller spinlock
* before being called.
*/
static inline int i8259A_irq_real(unsigned int irq)
{
int value;
int irqmask = 1<<irq;
if (irq < 8) {
outb(0x0B,0x20); /* ISR register */
value = inb(0x20) & irqmask;
outb(0x0A,0x20); /* back to the IRR register */
return value;
}
outb(0x0B,0xA0); /* ISR register */
value = inb(0xA0) & (irqmask >> 8);
outb(0x0A,0xA0); /* back to the IRR register */
return value;
}
/*
* Careful! The 8259A is a fragile beast, it pretty
* much _has_ to be done exactly like this (mask it
* first, _then_ send the EOI, and the order of EOI
* to the two 8259s is important!
*/
void mask_and_ack_8259A(unsigned int irq)
{
unsigned int irqmask = 1 << irq;
unsigned long flags;
spin_lock_irqsave(&i8259A_lock, flags);
/*
* Lightweight spurious IRQ detection. We do not want
* to overdo spurious IRQ handling - it's usually a sign
* of hardware problems, so we only do the checks we can
* do without slowing down good hardware unnecesserily.
*
* Note that IRQ7 and IRQ15 (the two spurious IRQs
* usually resulting from the 8259A-1|2 PICs) occur
* even if the IRQ is masked in the 8259A. Thus we
* can check spurious 8259A IRQs without doing the
* quite slow i8259A_irq_real() call for every IRQ.
* This does not cover 100% of spurious interrupts,
* but should be enough to warn the user that there
* is something bad going on ...
*/
if (cached_irq_mask & irqmask)
goto spurious_8259A_irq;
cached_irq_mask |= irqmask;
handle_real_irq:
if (irq & 8) {
inb(0xA1); /* DUMMY - (do we need this?) */
outb(cached_A1,0xA1);
outb(0x60+(irq&7),0xA0);/* 'Specific EOI' to slave */
outb(0x62,0x20); /* 'Specific EOI' to master-IRQ2 */
} else {
inb(0x21); /* DUMMY - (do we need this?) */
outb(cached_21,0x21);
outb(0x60+irq,0x20); /* 'Specific EOI' to master */
}
spin_unlock_irqrestore(&i8259A_lock, flags);
return;
spurious_8259A_irq:
/*
* this is the slow path - should happen rarely.
*/
if (i8259A_irq_real(irq))
/*
* oops, the IRQ _is_ in service according to the
* 8259A - not spurious, go handle it.
*/
goto handle_real_irq;
{
static int spurious_irq_mask;
/*
* At this point we can be sure the IRQ is spurious,
* lets ACK and report it. [once per IRQ]
*/
if (!(spurious_irq_mask & irqmask)) {
printk("spurious 8259A interrupt: IRQ%d.\n", irq);
spurious_irq_mask |= irqmask;
}
atomic_inc(&irq_err_count);
/*
* Theoretically we do not have to handle this IRQ,
* but in Linux this does not cause problems and is
* simpler for us.
*/
goto handle_real_irq;
}
}
void __init init_8259A(int auto_eoi)
{
unsigned long flags;
spin_lock_irqsave(&i8259A_lock, flags);
outb(0xff, 0x21); /* mask all of 8259A-1 */
outb(0xff, 0xA1); /* mask all of 8259A-2 */
/*
* outb_p - this has to work on a wide range of PC hardware.
*/
outb_p(0x11, 0x20); /* ICW1: select 8259A-1 init */
outb_p(0x20 + 0, 0x21); /* ICW2: 8259A-1 IR0-7 mapped to 0x20-0x27 */
outb_p(0x04, 0x21); /* 8259A-1 (the master) has a slave on IR2 */
if (auto_eoi)
outb_p(0x03, 0x21); /* master does Auto EOI */
else
outb_p(0x01, 0x21); /* master expects normal EOI */
outb_p(0x11, 0xA0); /* ICW1: select 8259A-2 init */
outb_p(0x20 + 8, 0xA1); /* ICW2: 8259A-2 IR0-7 mapped to 0x28-0x2f */
outb_p(0x02, 0xA1); /* 8259A-2 is a slave on master's IR2 */
outb_p(0x01, 0xA1); /* (slave's support for AEOI in flat mode
is to be investigated) */
if (auto_eoi)
/*
* in AEOI mode we just have to mask the interrupt
* when acking.
*/
i8259A_irq_type.ack = disable_8259A_irq;
else
i8259A_irq_type.ack = mask_and_ack_8259A;
udelay(100); /* wait for 8259A to initialize */
outb(cached_21, 0x21); /* restore master IRQ mask */
outb(cached_A1, 0xA1); /* restore slave IRQ mask */
spin_unlock_irqrestore(&i8259A_lock, flags);
}
/*
* IRQ2 is cascade interrupt to second interrupt controller
*/
static struct irqaction irq2 = { no_action, 0, 0, "cascade", NULL, NULL};
void __init init_ISA_irqs (void)
{
int i;
#ifdef CONFIG_X86_LOCAL_APIC
init_bsp_APIC();
#endif
init_8259A(0);
for (i = 0; i < NR_IRQS; i++) {
irq_desc[i].status = IRQ_DISABLED;
irq_desc[i].action = 0;
irq_desc[i].depth = 1;
if (i < 16) {
/*
* 16 old-style INTA-cycle interrupts:
*/
irq_desc[i].handler = &i8259A_irq_type;
} else {
/*
* 'high' PCI IRQs filled in on demand
*/
irq_desc[i].handler = &no_irq_type;
}
}
}
void __init init_IRQ(void)
{
int i;
init_ISA_irqs();
/*
* Cover the whole vector space, no vector can escape
* us. (some of these will be overridden and become
* 'special' SMP interrupts)
*/
for (i = 0; i < NR_IRQS; i++) {
int vector = FIRST_EXTERNAL_VECTOR + i;
if (vector != HYPERVISOR_CALL_VECTOR)
set_intr_gate(vector, interrupt[i]);
}
#ifdef CONFIG_SMP
/*
* IRQ0 must be given a fixed assignment and initialized,
* because it's used before the IO-APIC is set up.
*/
set_intr_gate(FIRST_DEVICE_VECTOR, interrupt[0]);
/*
* The reschedule interrupt is a CPU-to-CPU reschedule-helper
* IPI, driven by wakeup.
*/
set_intr_gate(EVENT_CHECK_VECTOR, event_check_interrupt);
/* IPI for invalidation */
set_intr_gate(INVALIDATE_TLB_VECTOR, invalidate_interrupt);
/* IPI for generic function call */
set_intr_gate(CALL_FUNCTION_VECTOR, call_function_interrupt);
#endif
#ifdef CONFIG_X86_LOCAL_APIC
/* self generated IPI for local APIC timer */
set_intr_gate(LOCAL_TIMER_VECTOR, apic_timer_interrupt);
/* IPI vectors for APIC spurious and error interrupts */
set_intr_gate(SPURIOUS_APIC_VECTOR, spurious_interrupt);
set_intr_gate(ERROR_APIC_VECTOR, error_interrupt);
#endif
/*
* Set the clock to HZ Hz, we already have a valid
* vector now:
*/
#define CLOCK_TICK_RATE 1193180 /* crystal freq (Hz) */
#define LATCH (((CLOCK_TICK_RATE)+(HZ/2))/HZ)
outb_p(0x34,0x43); /* binary, mode 2, LSB/MSB, ch 0 */
outb_p(LATCH & 0xff , 0x40); /* LSB */
outb(LATCH >> 8 , 0x40); /* MSB */
setup_irq(2, &irq2);
}
+20
View File
@@ -0,0 +1,20 @@
#include <xeno/config.h>
#include <xeno/sched.h>
#include <asm/desc.h>
/*
* Initial task structure. XXX KAF: To get this 8192-byte aligned without
* linker tricks I copy it into aligned BSS area at boot time.
* Actual name idle0_task_union now declared in boot.S.
*/
struct task_struct first_task_struct = IDLE0_TASK(idle0_task_union.task);
/*
* per-CPU TSS segments. Threads are completely 'soft' on Linux,
* no more per-task TSS's. The TSS size is kept cacheline-aligned
* so they are allowed to end up in the .data.cacheline_aligned
* section. Since TSS's are completely CPU-local, we want them
* on exact cacheline boundaries, to eliminate cacheline ping-pong.
*/
struct tss_struct init_tss[NR_CPUS] __cacheline_aligned = { [0 ... NR_CPUS-1] = INIT_TSS };
File diff suppressed because it is too large. Load diff
+106
View File
@@ -0,0 +1,106 @@
/*
* arch/i386/mm/ioremap.c
*
* Re-map IO memory to kernel address space so that we can access it.
* This is needed for high PCI addresses that aren't mapped in the
* 640k-1MB IO memory area on PC's
*
* (C) Copyright 1995 1996 Linus Torvalds
*/
//#include <linux/vmalloc.h>
#include <asm/io.h>
#include <asm/pgalloc.h>
#include <asm/page.h>
static unsigned long remap_base = 0;
#define L2_PROT (_PAGE_PRESENT|_PAGE_RW|_PAGE_ACCESSED)
#define L1_PROT (_PAGE_PRESENT|_PAGE_RW|_PAGE_ACCESSED|_PAGE_DIRTY)
#define PAGE_ALIGN(addr) (((addr)+PAGE_SIZE-1)&PAGE_MASK)
static void new_l2e(l2_pgentry_t *pl2e)
{
l1_pgentry_t *pl1e = (l1_pgentry_t *)get_free_page(GFP_KERNEL);
if ( !pl1e ) BUG();
clear_page(pl1e);
*pl2e = mk_l2_pgentry(__pa(pl1e)|L2_PROT);
}
void * __ioremap(unsigned long phys_addr, unsigned long size, unsigned long flags)
{
unsigned long vaddr;
unsigned long offset, cur=0, last_addr;
l2_pgentry_t *pl2e;
l1_pgentry_t *pl1e;
/* First time through, start allocating from end of real memory. */
if ( !remap_base )
remap_base = (unsigned long)phys_to_virt(MAX_USABLE_ADDRESS);
/* Don't allow wraparound or zero size */
last_addr = phys_addr + size - 1;
if (!size || last_addr < phys_addr)
return NULL;
/*
* Don't remap the low PCI/ISA area, it's always mapped..
*/
if (phys_addr >= 0xA0000 && last_addr < 0x100000)
return phys_to_virt(phys_addr);
#if 0
/*
* Don't allow anybody to remap normal RAM that we're using..
*/
if (phys_addr < virt_to_phys(high_memory)) {
char *t_addr, *t_end;
struct pfn_info *page;
t_addr = __va(phys_addr);
t_end = t_addr + (size - 1);
for(page = virt_to_page(t_addr); page <= virt_to_page(t_end); page++)
if(!PageReserved(page))
return NULL;
}
#endif
/*
* Mappings have to be page-aligned
*/
offset = phys_addr & ~PAGE_MASK;
phys_addr &= PAGE_MASK;
size = PAGE_ALIGN(last_addr) - phys_addr;
/*
* Ok, go for it..
*/
vaddr = remap_base;
remap_base += size;
pl2e = idle0_pg_table + l2_table_offset(vaddr);
if ( l2_pgentry_empty(*pl2e) ) new_l2e(pl2e);
pl1e = l2_pgentry_to_l1(*pl2e++) + l1_table_offset(vaddr);
for ( ; ; )
{
if ( !l1_pgentry_empty(*pl1e) ) BUG();
*pl1e++ = mk_l1_pgentry((phys_addr+cur)|L1_PROT|flags);
cur += PAGE_SIZE;
if ( cur == size ) break;
if ( !((unsigned long)pl1e & (PAGE_SIZE-1)) )
{
if ( l2_pgentry_empty(*pl2e) ) new_l2e(pl2e);
pl1e = l2_pgentry_to_l1(*pl2e++);
}
}
flush_tlb_all();
return (void *) (offset + (char *)vaddr);
}
void iounmap(void *addr)
{
/* NOP for now. */
}
+895
View File
@@ -0,0 +1,895 @@
/*
* linux/arch/i386/kernel/irq.c
*
* Copyright (C) 1992, 1998 Linus Torvalds, Ingo Molnar
*
* This file contains the code used by various IRQ handling routines:
* asking for different IRQ's should be done through these routines
* instead of just grabbing them. Thus setup_irqs with different IRQ numbers
* shouldn't result in any weird surprises, and installing new handlers
* should be easier.
*/
/*
* (mostly architecture independent, will move to kernel/irq.c in 2.5.)
*
* IRQs are in fact implemented a bit like signal handlers for the kernel.
* Naturally it's not a 1:1 relation, but there are similarities.
*/
#include <xeno/config.h>
#include <xeno/init.h>
#include <xeno/errno.h>
#include <xeno/sched.h>
#include <xeno/interrupt.h>
#include <xeno/irq.h>
#include <xeno/slab.h>
#include <asm/msr.h>
#include <asm/hardirq.h>
#include <asm/ptrace.h>
#include <asm/atomic.h>
#include <asm/io.h>
#include <asm/smp.h>
#include <asm/system.h>
#include <asm/bitops.h>
#include <asm/pgalloc.h>
#include <xeno/delay.h>
/*
* Linux has a controller-independent x86 interrupt architecture.
* every controller has a 'controller-template', that is used
* by the main code to do the right thing. Each driver-visible
* interrupt source is transparently wired to the apropriate
* controller. Thus drivers need not be aware of the
* interrupt-controller.
*
* Various interrupt controllers we handle: 8259 PIC, SMP IO-APIC,
* PIIX4's internal 8259 PIC and SGI's Visual Workstation Cobalt (IO-)APIC.
* (IO-APICs assumed to be messaging to Pentium local-APICs)
*
* the code is designed to be easily extended with new/different
* interrupt controllers, without having to do assembly magic.
*/
/*
* Controller mappings for all interrupt sources:
*/
irq_desc_t irq_desc[NR_IRQS] __cacheline_aligned =
{ [0 ... NR_IRQS-1] = { 0, &no_irq_type, NULL, 0, SPIN_LOCK_UNLOCKED}};
/*
* Special irq handlers.
*/
void no_action(int cpl, void *dev_id, struct pt_regs *regs) { }
/*
* Generic no controller code
*/
static void enable_none(unsigned int irq) { }
static unsigned int startup_none(unsigned int irq) { return 0; }
static void disable_none(unsigned int irq) { }
static void ack_none(unsigned int irq)
{
/*
* 'what should we do if we get a hw irq event on an illegal vector'.
* each architecture has to answer this themselves, it doesnt deserve
* a generic callback i think.
*/
#if CONFIG_X86
printk("unexpected IRQ trap at vector %02x\n", irq);
#ifdef CONFIG_X86_LOCAL_APIC
/*
* Currently unexpected vectors happen only on SMP and APIC.
* We _must_ ack these because every local APIC has only N
* irq slots per priority level, and a 'hanging, unacked' IRQ
* holds up an irq slot - in excessive cases (when multiple
* unexpected vectors occur) that might lock up the APIC
* completely.
*/
ack_APIC_irq();
#endif
#endif
}
/* startup is the same as "enable", shutdown is same as "disable" */
#define shutdown_none disable_none
#define end_none enable_none
struct hw_interrupt_type no_irq_type = {
"none",
startup_none,
shutdown_none,
enable_none,
disable_none,
ack_none,
end_none
};
atomic_t irq_err_count;
#ifdef CONFIG_X86_IO_APIC
#ifdef APIC_MISMATCH_DEBUG
atomic_t irq_mis_count;
#endif
#endif
/*
* Generic, controller-independent functions:
*/
/*
* Global interrupt locks for SMP. Allow interrupts to come in on any
* CPU, yet make cli/sti act globally to protect critical regions..
*/
#ifdef CONFIG_SMP
unsigned char global_irq_holder = 0xff;
unsigned volatile long global_irq_lock; /* pendantic: long for set_bit --RR */
#define MAXCOUNT 100000000
/*
* I had a lockup scenario where a tight loop doing
* spin_unlock()/spin_lock() on CPU#1 was racing with
* spin_lock() on CPU#0. CPU#0 should have noticed spin_unlock(), but
* apparently the spin_unlock() information did not make it
* through to CPU#0 ... nasty, is this by design, do we have to limit
* 'memory update oscillation frequency' artificially like here?
*
* Such 'high frequency update' races can be avoided by careful design, but
* some of our major constructs like spinlocks use similar techniques,
* it would be nice to clarify this issue. Set this define to 0 if you
* want to check whether your system freezes. I suspect the delay done
* by SYNC_OTHER_CORES() is in correlation with 'snooping latency', but
* i thought that such things are guaranteed by design, since we use
* the 'LOCK' prefix.
*/
#define SUSPECTED_CPU_OR_CHIPSET_BUG_WORKAROUND 0
#if SUSPECTED_CPU_OR_CHIPSET_BUG_WORKAROUND
# define SYNC_OTHER_CORES(x) udelay(x+1)
#else
/*
* We have to allow irqs to arrive between __sti and __cli
*/
# define SYNC_OTHER_CORES(x) __asm__ __volatile__ ("nop")
#endif
static inline void wait_on_irq(int cpu)
{
for (;;) {
/*
* Wait until all interrupts are gone. Wait
* for bottom half handlers unless we're
* already executing in one..
*/
if (!irqs_running())
if (local_bh_count(cpu) || !spin_is_locked(&global_bh_lock))
break;
/* Duh, we have to loop. Release the lock to avoid deadlocks */
clear_bit(0,&global_irq_lock);
for (;;) {
__sti();
SYNC_OTHER_CORES(cpu);
__cli();
if (irqs_running())
continue;
if (global_irq_lock)
continue;
if (!local_bh_count(cpu) && spin_is_locked(&global_bh_lock))
continue;
if (!test_and_set_bit(0,&global_irq_lock))
break;
}
}
}
/*
* This is called when we want to synchronize with
* interrupts. We may for example tell a device to
* stop sending interrupts: but to make sure there
* are no interrupts that are executing on another
* CPU we need to call this function.
*/
void synchronize_irq(void)
{
if (irqs_running()) {
/* Stupid approach */
cli();
sti();
}
}
static inline void get_irqlock(int cpu)
{
if (test_and_set_bit(0,&global_irq_lock)) {
/* do we already hold the lock? */
if ((unsigned char) cpu == global_irq_holder)
return;
/* Uhhuh.. Somebody else got it. Wait.. */
do {
do {
rep_nop();
} while (test_bit(0,&global_irq_lock));
} while (test_and_set_bit(0,&global_irq_lock));
}
/*
* We also to make sure that nobody else is running
* in an interrupt context.
*/
wait_on_irq(cpu);
/*
* Ok, finally..
*/
global_irq_holder = cpu;
}
#define EFLAGS_IF_SHIFT 9
/*
* A global "cli()" while in an interrupt context
* turns into just a local cli(). Interrupts
* should use spinlocks for the (very unlikely)
* case that they ever want to protect against
* each other.
*
* If we already have local interrupts disabled,
* this will not turn a local disable into a
* global one (problems with spinlocks: this makes
* save_flags+cli+sti usable inside a spinlock).
*/
void __global_cli(void)
{
unsigned int flags;
__save_flags(flags);
if (flags & (1 << EFLAGS_IF_SHIFT)) {
int cpu = smp_processor_id();
__cli();
if (!local_irq_count(cpu))
get_irqlock(cpu);
}
}
void __global_sti(void)
{
int cpu = smp_processor_id();
if (!local_irq_count(cpu))
release_irqlock(cpu);
__sti();
}
/*
* SMP flags value to restore to:
* 0 - global cli
* 1 - global sti
* 2 - local cli
* 3 - local sti
*/
unsigned long __global_save_flags(void)
{
int retval;
int local_enabled;
unsigned long flags;
int cpu = smp_processor_id();
__save_flags(flags);
local_enabled = (flags >> EFLAGS_IF_SHIFT) & 1;
/* default to local */
retval = 2 + local_enabled;
/* check for global flags if we're not in an interrupt */
if (!local_irq_count(cpu)) {
if (local_enabled)
retval = 1;
if (global_irq_holder == cpu)
retval = 0;
}
return retval;
}
void __global_restore_flags(unsigned long flags)
{
switch (flags) {
case 0:
__global_cli();
break;
case 1:
__global_sti();
break;
case 2:
__cli();
break;
case 3:
__sti();
break;
default:
printk("global_restore_flags: %08lx (%08lx)\n",
flags, (&flags)[-1]);
}
}
#endif
/*
* This should really return information about whether
* we should do bottom half handling etc. Right now we
* end up _always_ checking the bottom half, which is a
* waste of time and is not what some drivers would
* prefer.
*/
int handle_IRQ_event(unsigned int irq, struct pt_regs * regs, struct irqaction * action)
{
int status;
int cpu = smp_processor_id();
irq_enter(cpu, irq);
status = 1; /* Force the "do bottom halves" bit */
if (!(action->flags & SA_INTERRUPT))
__sti();
do {
status |= action->flags;
action->handler(irq, action->dev_id, regs);
action = action->next;
} while (action);
__cli();
irq_exit(cpu, irq);
return status;
}
/*
* Generic enable/disable code: this just calls
* down into the PIC-specific version for the actual
* hardware disable after having gotten the irq
* controller lock.
*/
/**
* disable_irq_nosync - disable an irq without waiting
* @irq: Interrupt to disable
*
* Disable the selected interrupt line. Disables and Enables are
* nested.
* Unlike disable_irq(), this function does not ensure existing
* instances of the IRQ handler have completed before returning.
*
* This function may be called from IRQ context.
*/
inline void disable_irq_nosync(unsigned int irq)
{
irq_desc_t *desc = irq_desc + irq;
unsigned long flags;
spin_lock_irqsave(&desc->lock, flags);
if (!desc->depth++) {
desc->status |= IRQ_DISABLED;
desc->handler->disable(irq);
}
spin_unlock_irqrestore(&desc->lock, flags);
}
/**
* disable_irq - disable an irq and wait for completion
* @irq: Interrupt to disable
*
* Disable the selected interrupt line. Enables and Disables are
* nested.
* This function waits for any pending IRQ handlers for this interrupt
* to complete before returning. If you use this function while
* holding a resource the IRQ handler may need you will deadlock.
*
* This function may be called - with care - from IRQ context.
*/
void disable_irq(unsigned int irq)
{
disable_irq_nosync(irq);
if (!local_irq_count(smp_processor_id())) {
do {
barrier();
cpu_relax();
} while (irq_desc[irq].status & IRQ_INPROGRESS);
}
}
/**
* enable_irq - enable handling of an irq
* @irq: Interrupt to enable
*
* Undoes the effect of one call to disable_irq(). If this
* matches the last disable, processing of interrupts on this
* IRQ line is re-enabled.
*
* This function may be called from IRQ context.
*/
void enable_irq(unsigned int irq)
{
irq_desc_t *desc = irq_desc + irq;
unsigned long flags;
spin_lock_irqsave(&desc->lock, flags);
switch (desc->depth) {
case 1: {
unsigned int status = desc->status & ~IRQ_DISABLED;
desc->status = status;
if ((status & (IRQ_PENDING | IRQ_REPLAY)) == IRQ_PENDING) {
desc->status = status | IRQ_REPLAY;
hw_resend_irq(desc->handler,irq);
}
desc->handler->enable(irq);
/* fall-through */
}
default:
desc->depth--;
break;
case 0:
printk("enable_irq(%u) unbalanced from %p\n", irq,
__builtin_return_address(0));
}
spin_unlock_irqrestore(&desc->lock, flags);
}
/*
* do_IRQ handles all normal device IRQ's (the special
* SMP cross-CPU interrupts have their own specific
* handlers).
*/
asmlinkage unsigned int do_IRQ(struct pt_regs regs)
{
/*
* We ack quickly, we don't want the irq controller
* thinking we're snobs just because some other CPU has
* disabled global interrupts (we have already done the
* INT_ACK cycles, it's too late to try to pretend to the
* controller that we aren't taking the interrupt).
*
* 0 return value means that this irq is already being
* handled by some other CPU. (or is disabled)
*/
int irq = regs.orig_eax & 0xff; /* high bits used in ret_from_ code */
int cpu = smp_processor_id();
irq_desc_t *desc = irq_desc + irq;
struct irqaction * action;
unsigned int status;
spin_lock(&desc->lock);
desc->handler->ack(irq);
/*
REPLAY is when Linux resends an IRQ that was dropped earlier
WAITING is used by probe to mark irqs that are being tested
*/
status = desc->status & ~(IRQ_REPLAY | IRQ_WAITING);
status |= IRQ_PENDING; /* we _want_ to handle it */
/*
* If the IRQ is disabled for whatever reason, we cannot
* use the action we have.
*/
action = NULL;
if (!(status & (IRQ_DISABLED | IRQ_INPROGRESS))) {
action = desc->action;
status &= ~IRQ_PENDING; /* we commit to handling */
status |= IRQ_INPROGRESS; /* we are handling it */
}
desc->status = status;
/*
* If there is no IRQ handler or it was disabled, exit early.
Since we set PENDING, if another processor is handling
a different instance of this same irq, the other processor
will take care of it.
*/
if (!action)
goto out;
/*
* Edge triggered interrupts need to remember
* pending events.
* This applies to any hw interrupts that allow a second
* instance of the same irq to arrive while we are in do_IRQ
* or in the handler. But the code here only handles the _second_
* instance of the irq, not the third or fourth. So it is mostly
* useful for irq hardware that does not mask cleanly in an
* SMP environment.
*/
for (;;) {
spin_unlock(&desc->lock);
handle_IRQ_event(irq, &regs, action);
spin_lock(&desc->lock);
if (!(desc->status & IRQ_PENDING))
break;
desc->status &= ~IRQ_PENDING;
}
desc->status &= ~IRQ_INPROGRESS;
out:
/*
* The ->end() handler has to deal with interrupts which got
* disabled while the handler was running.
*/
desc->handler->end(irq);
spin_unlock(&desc->lock);
if (softirq_pending(cpu))
do_softirq();
return 1;
}
/**
* request_irq - allocate an interrupt line
* @irq: Interrupt line to allocate
* @handler: Function to be called when the IRQ occurs
* @irqflags: Interrupt type flags
* @devname: An ascii name for the claiming device
* @dev_id: A cookie passed back to the handler function
*
* This call allocates interrupt resources and enables the
* interrupt line and IRQ handling. From the point this
* call is made your handler function may be invoked. Since
* your handler function must clear any interrupt the board
* raises, you must take care both to initialise your hardware
* and to set up the interrupt handler in the right order.
*
* Dev_id must be globally unique. Normally the address of the
* device data structure is used as the cookie. Since the handler
* receives this value it makes sense to use it.
*
* If your interrupt is shared you must pass a non NULL dev_id
* as this is required when freeing the interrupt.
*
* Flags:
*
* SA_SHIRQ Interrupt is shared
*
* SA_INTERRUPT Disable local interrupts while processing
*/
int request_irq(unsigned int irq,
void (*handler)(int, void *, struct pt_regs *),
unsigned long irqflags,
const char * devname,
void *dev_id)
{
int retval;
struct irqaction * action;
if (irq >= NR_IRQS)
return -EINVAL;
if (!handler)
return -EINVAL;
action = (struct irqaction *)
kmalloc(sizeof(struct irqaction), GFP_KERNEL);
if (!action)
return -ENOMEM;
action->handler = handler;
action->flags = irqflags;
action->mask = 0;
action->name = devname;
action->next = NULL;
action->dev_id = dev_id;
retval = setup_irq(irq, action);
if (retval)
kfree(action);
return retval;
}
/**
* free_irq - free an interrupt
* @irq: Interrupt line to free
* @dev_id: Device identity to free
*
* Remove an interrupt handler. The handler is removed and if the
* interrupt line is no longer in use by any driver it is disabled.
* On a shared IRQ the caller must ensure the interrupt is disabled
* on the card it drives before calling this function. The function
* does not return until any executing interrupts for this IRQ
* have completed.
*
* This function may be called from interrupt context.
*
* Bugs: Attempting to free an irq in a handler for the same irq hangs
* the machine.
*/
void free_irq(unsigned int irq, void *dev_id)
{
irq_desc_t *desc;
struct irqaction **p;
unsigned long flags;
if (irq >= NR_IRQS)
return;
desc = irq_desc + irq;
spin_lock_irqsave(&desc->lock,flags);
p = &desc->action;
for (;;) {
struct irqaction * action = *p;
if (action) {
struct irqaction **pp = p;
p = &action->next;
if (action->dev_id != dev_id)
continue;
/* Found it - now remove it from the list of entries */
*pp = action->next;
if (!desc->action) {
desc->status |= IRQ_DISABLED;
desc->handler->shutdown(irq);
}
spin_unlock_irqrestore(&desc->lock,flags);
#ifdef CONFIG_SMP
/* Wait to make sure it's not being used on another CPU */
while (desc->status & IRQ_INPROGRESS) {
barrier();
cpu_relax();
}
#endif
kfree(action);
return;
}
printk("Trying to free free IRQ%d\n",irq);
spin_unlock_irqrestore(&desc->lock,flags);
return;
}
}
/*
* IRQ autodetection code..
*
* This depends on the fact that any interrupt that
* comes in on to an unassigned handler will get stuck
* with "IRQ_WAITING" cleared and the interrupt
* disabled.
*/
static spinlock_t probe_sem = SPIN_LOCK_UNLOCKED;
/**
* probe_irq_on - begin an interrupt autodetect
*
* Commence probing for an interrupt. The interrupts are scanned
* and a mask of potential interrupt lines is returned.
*
*/
unsigned long probe_irq_on(void)
{
unsigned int i;
irq_desc_t *desc;
unsigned long val;
unsigned long s=0, e=0;
spin_lock(&probe_sem);
/*
* something may have generated an irq long ago and we want to
* flush such a longstanding irq before considering it as spurious.
*/
for (i = NR_IRQS-1; i > 0; i--) {
desc = irq_desc + i;
spin_lock_irq(&desc->lock);
if (!irq_desc[i].action)
irq_desc[i].handler->startup(i);
spin_unlock_irq(&desc->lock);
}
/* Wait for longstanding interrupts to trigger (20ms delay). */
rdtscl(s);
do {
synchronize_irq();
rdtscl(e);
} while ( ((e-s)/ticks_per_usec) < 20000 );
/*
* enable any unassigned irqs
* (we must startup again here because if a longstanding irq
* happened in the previous stage, it may have masked itself)
*/
for (i = NR_IRQS-1; i > 0; i--) {
desc = irq_desc + i;
spin_lock_irq(&desc->lock);
if (!desc->action) {
desc->status |= IRQ_AUTODETECT | IRQ_WAITING;
if (desc->handler->startup(i))
desc->status |= IRQ_PENDING;
}
spin_unlock_irq(&desc->lock);
}
/*
* Wait for spurious interrupts to trigger (100ms delay).
*/
rdtscl(s);
do {
synchronize_irq();
rdtscl(e);
} while ( ((e-s)/ticks_per_usec) < 100000 );
/*
* Now filter out any obviously spurious interrupts
*/
val = 0;
for (i = 0; i < NR_IRQS; i++) {
irq_desc_t *desc = irq_desc + i;
unsigned int status;
spin_lock_irq(&desc->lock);
status = desc->status;
if (status & IRQ_AUTODETECT) {
/* It triggered already - consider it spurious. */
if (!(status & IRQ_WAITING)) {
desc->status = status & ~IRQ_AUTODETECT;
desc->handler->shutdown(i);
} else
if (i < 32)
val |= 1 << i;
}
spin_unlock_irq(&desc->lock);
}
return val;
}
/*
* Return a mask of triggered interrupts (this
* can handle only legacy ISA interrupts).
*/
/**
* probe_irq_mask - scan a bitmap of interrupt lines
* @val: mask of interrupts to consider
*
* Scan the ISA bus interrupt lines and return a bitmap of
* active interrupts. The interrupt probe logic state is then
* returned to its previous value.
*
* Note: we need to scan all the irq's even though we will
* only return ISA irq numbers - just so that we reset them
* all to a known state.
*/
unsigned int probe_irq_mask(unsigned long val)
{
int i;
unsigned int mask;
mask = 0;
for (i = 0; i < NR_IRQS; i++) {
irq_desc_t *desc = irq_desc + i;
unsigned int status;
spin_lock_irq(&desc->lock);
status = desc->status;
if (status & IRQ_AUTODETECT) {
if (i < 16 && !(status & IRQ_WAITING))
mask |= 1 << i;
desc->status = status & ~IRQ_AUTODETECT;
desc->handler->shutdown(i);
}
spin_unlock_irq(&desc->lock);
}
spin_unlock(&probe_sem);
return mask & val;
}
/*
* Return the one interrupt that triggered (this can
* handle any interrupt source).
*/
/**
* probe_irq_off - end an interrupt autodetect
* @val: mask of potential interrupts (unused)
*
* Scans the unused interrupt lines and returns the line which
* appears to have triggered the interrupt. If no interrupt was
* found then zero is returned. If more than one interrupt is
* found then minus the first candidate is returned to indicate
* their is doubt.
*
* The interrupt probe logic state is returned to its previous
* value.
*
* BUGS: When used in a module (which arguably shouldnt happen)
* nothing prevents two IRQ probe callers from overlapping. The
* results of this are non-optimal.
*/
int probe_irq_off(unsigned long val)
{
int i, irq_found, nr_irqs;
nr_irqs = 0;
irq_found = 0;
for (i = 0; i < NR_IRQS; i++) {
irq_desc_t *desc = irq_desc + i;
unsigned int status;
spin_lock_irq(&desc->lock);
status = desc->status;
if (status & IRQ_AUTODETECT) {
if (!(status & IRQ_WAITING)) {
if (!nr_irqs)
irq_found = i;
nr_irqs++;
}
desc->status = status & ~IRQ_AUTODETECT;
desc->handler->shutdown(i);
}
spin_unlock_irq(&desc->lock);
}
spin_unlock(&probe_sem);
if (nr_irqs > 1)
irq_found = -irq_found;
return irq_found;
}
/* this was setup_x86_irq but it seems pretty generic */
int setup_irq(unsigned int irq, struct irqaction * new)
{
int shared = 0;
unsigned long flags;
struct irqaction *old, **p;
irq_desc_t *desc = irq_desc + irq;
/*
* The following block of code has to be executed atomically
*/
spin_lock_irqsave(&desc->lock,flags);
p = &desc->action;
if ((old = *p) != NULL) {
/* Can't share interrupts unless both agree to */
if (!(old->flags & new->flags & SA_SHIRQ)) {
spin_unlock_irqrestore(&desc->lock,flags);
return -EBUSY;
}
/* add new interrupt at end of irq queue */
do {
p = &old->next;
old = *p;
} while (old);
shared = 1;
}
*p = new;
if (!shared) {
desc->depth = 0;
desc->status &= ~(IRQ_DISABLED | IRQ_AUTODETECT | IRQ_WAITING);
desc->handler->startup(irq);
}
spin_unlock_irqrestore(&desc->lock,flags);
return 0;
}
+96
View File
@@ -0,0 +1,96 @@
#include <xeno/config.h>
#include <xeno/lib.h>
#include <xeno/init.h>
#include <xeno/mm.h>
#include <asm/page.h>
#include <asm/pgalloc.h>
#include <asm/fixmap.h>
static inline void set_pte_phys (unsigned long vaddr,
l1_pgentry_t entry)
{
l2_pgentry_t *l2ent;
l1_pgentry_t *l1ent;
l2ent = idle0_pg_table + l2_table_offset(vaddr);
l1ent = l2_pgentry_to_l1(*l2ent) + l1_table_offset(vaddr);
*l1ent = entry;
/* It's enough to flush this one mapping. */
__flush_tlb_one(vaddr);
}
void __set_fixmap (enum fixed_addresses idx,
l1_pgentry_t entry)
{
unsigned long address = __fix_to_virt(idx);
if (idx >= __end_of_fixed_addresses) {
printk("Invalid __set_fixmap\n");
return;
}
set_pte_phys(address, entry);
}
static void __init fixrange_init (unsigned long start,
unsigned long end, l2_pgentry_t *pg_base)
{
l2_pgentry_t *l2e;
int i;
unsigned long vaddr, page;
vaddr = start;
i = l2_table_offset(vaddr);
l2e = pg_base + i;
for ( ; (i < ENTRIES_PER_L2_PAGETABLE) && (vaddr != end); l2e++, i++ )
{
if ( !l2_pgentry_empty(*l2e) ) continue;
page = (unsigned long)get_free_page(GFP_KERNEL);
clear_page(page);
*l2e = mk_l2_pgentry(__pa(page) | PAGE_HYPERVISOR);
vaddr += 1 << L2_PAGETABLE_SHIFT;
}
}
void __init paging_init(void)
{
unsigned long addr;
/* XXX initialised in boot.S */
/*if ( cpu_has_pge ) set_in_cr4(X86_CR4_PGE);*/
/*if ( cpu_has_pse ) set_in_cr4(X86_CR4_PSE);*/
/*if ( cpu_has_pae ) set_in_cr4(X86_CR4_PAE);*/
/*
* Fixed mappings, only the page table structure has to be
* created - mappings will be set by set_fixmap():
*/
addr = FIXADDR_START & ~((1<<L2_PAGETABLE_SHIFT)-1);
fixrange_init(addr, 0, idle0_pg_table);
}
void __init zap_low_mappings (void)
{
int i;
for (i = 0; i < DOMAIN_ENTRIES_PER_L2_PAGETABLE; i++ )
idle0_pg_table[i] = mk_l2_pgentry(0);
flush_tlb_all();
}
long do_set_guest_stack(unsigned long ss, unsigned long esp)
{
int nr = smp_processor_id();
struct tss_struct *t = &init_tss[nr];
if ( (ss == __HYPERVISOR_CS) || (ss == __HYPERVISOR_DS) )
return -1;
current->thread.ss1 = ss;
current->thread.esp1 = esp;
t->ss1 = ss;
t->esp1 = esp;
return 0;
}
+630
View File
@@ -0,0 +1,630 @@
/*
* Intel Multiprocessor Specificiation 1.1 and 1.4
* compliant MP-table parsing routines.
*
* (c) 1995 Alan Cox, Building #3 <alan@redhat.com>
* (c) 1998, 1999, 2000 Ingo Molnar <mingo@redhat.com>
*
* Fixes
* Erich Boleyn : MP v1.4 and additional changes.
* Alan Cox : Added EBDA scanning
* Ingo Molnar : various cleanups and rewrites
* Maciej W. Rozycki : Bits for default MP configurations
*/
#include <xeno/config.h>
#include <xeno/init.h>
#include <xeno/lib.h>
#include <asm/io.h>
#include <xeno/irq.h>
#include <xeno/smp.h>
#include <asm/mpspec.h>
#include <asm/pgalloc.h>
/* Have we found an MP table */
int smp_found_config;
/*
* Various Linux-internal data structures created from the
* MP-table.
*/
int apic_version [MAX_APICS];
int mp_bus_id_to_type [MAX_MP_BUSSES];
int mp_bus_id_to_node [MAX_MP_BUSSES];
int mp_bus_id_to_pci_bus [MAX_MP_BUSSES] = { [0 ... MAX_MP_BUSSES-1] = -1 };
int mp_current_pci_id;
/* I/O APIC entries */
struct mpc_config_ioapic mp_ioapics[MAX_IO_APICS];
/* # of MP IRQ source entries */
struct mpc_config_intsrc mp_irqs[MAX_IRQ_SOURCES];
/* MP IRQ source entries */
int mp_irq_entries;
int nr_ioapics;
int pic_mode;
unsigned long mp_lapic_addr;
/* Processor that is doing the boot up */
unsigned int boot_cpu_physical_apicid = -1U;
unsigned int boot_cpu_logical_apicid = -1U;
/* Internal processor count */
static unsigned int num_processors;
/* Bitmask of physically existing CPUs */
unsigned long phys_cpu_present_map;
/*
* Intel MP BIOS table parsing routines:
*/
/*
* Checksum an MP configuration block.
*/
static int __init mpf_checksum(unsigned char *mp, int len)
{
int sum = 0;
while (len--)
sum += *mp++;
return sum & 0xFF;
}
/*
* Processor encoding in an MP configuration block
*/
static char __init *mpc_family(int family,int model)
{
static char n[32];
static char *model_defs[]=
{
"80486DX","80486DX",
"80486SX","80486DX/2 or 80487",
"80486SL","80486SX/2",
"Unknown","80486DX/2-WB",
"80486DX/4","80486DX/4-WB"
};
switch (family) {
case 0x04:
if (model < 10)
return model_defs[model];
break;
case 0x05:
return("Pentium(tm)");
case 0x06:
return("Pentium(tm) Pro");
case 0x0F:
if (model == 0x00)
return("Pentium 4(tm)");
if (model == 0x0F)
return("Special controller");
}
sprintf(n,"Unknown CPU [%d:%d]",family, model);
return n;
}
/*
* Have to match translation table entries to main table entries by counter
* hence the mpc_record variable .... can't see a less disgusting way of
* doing this ....
*/
static int mpc_record;
void __init MP_processor_info (struct mpc_config_processor *m)
{
int ver, logical_apicid;
if (!(m->mpc_cpuflag & CPU_ENABLED))
return;
logical_apicid = m->mpc_apicid;
printk("Processor #%d %s APIC version %d\n",
m->mpc_apicid,
mpc_family((m->mpc_cpufeature & CPU_FAMILY_MASK)>>8 ,
(m->mpc_cpufeature & CPU_MODEL_MASK)>>4),
m->mpc_apicver);
if (m->mpc_cpuflag & CPU_BOOTPROCESSOR) {
Dprintk(" Bootup CPU\n");
boot_cpu_physical_apicid = m->mpc_apicid;
boot_cpu_logical_apicid = logical_apicid;
}
num_processors++;
if (m->mpc_apicid > MAX_APICS) {
printk("Processor #%d INVALID. (Max ID: %d).\n",
m->mpc_apicid, MAX_APICS);
return;
}
ver = m->mpc_apicver;
phys_cpu_present_map |= 1 << m->mpc_apicid;
/*
* Validate version
*/
if (ver == 0x0) {
printk("BIOS bug, APIC version is 0 for CPU#%d! fixing up to 0x10. (tell your hw vendor)\n", m->mpc_apicid);
ver = 0x10;
}
apic_version[m->mpc_apicid] = ver;
}
static void __init MP_bus_info (struct mpc_config_bus *m)
{
char str[7];
memcpy(str, m->mpc_bustype, 6);
str[6] = 0;
Dprintk("Bus #%d is %s\n", m->mpc_busid, str);
if (strncmp(str, BUSTYPE_ISA, sizeof(BUSTYPE_ISA)-1) == 0) {
mp_bus_id_to_type[m->mpc_busid] = MP_BUS_ISA;
} else if (strncmp(str, BUSTYPE_EISA, sizeof(BUSTYPE_EISA)-1) == 0) {
mp_bus_id_to_type[m->mpc_busid] = MP_BUS_EISA;
} else if (strncmp(str, BUSTYPE_PCI, sizeof(BUSTYPE_PCI)-1) == 0) {
mp_bus_id_to_type[m->mpc_busid] = MP_BUS_PCI;
mp_bus_id_to_pci_bus[m->mpc_busid] = mp_current_pci_id;
mp_current_pci_id++;
} else if (strncmp(str, BUSTYPE_MCA, sizeof(BUSTYPE_MCA)-1) == 0) {
mp_bus_id_to_type[m->mpc_busid] = MP_BUS_MCA;
} else {
printk("Unknown bustype %s - ignoring\n", str);
}
}
static void __init MP_ioapic_info (struct mpc_config_ioapic *m)
{
if (!(m->mpc_flags & MPC_APIC_USABLE))
return;
printk("I/O APIC #%d Version %d at 0x%lX.\n",
m->mpc_apicid, m->mpc_apicver, m->mpc_apicaddr);
if (nr_ioapics >= MAX_IO_APICS) {
printk("Max # of I/O APICs (%d) exceeded (found %d).\n",
MAX_IO_APICS, nr_ioapics);
panic("Recompile kernel with bigger MAX_IO_APICS!.\n");
}
if (!m->mpc_apicaddr) {
printk("WARNING: bogus zero I/O APIC address"
" found in MP table, skipping!\n");
return;
}
mp_ioapics[nr_ioapics] = *m;
nr_ioapics++;
}
static void __init MP_intsrc_info (struct mpc_config_intsrc *m)
{
mp_irqs [mp_irq_entries] = *m;
Dprintk("Int: type %d, pol %d, trig %d, bus %d,"
" IRQ %02x, APIC ID %x, APIC INT %02x\n",
m->mpc_irqtype, m->mpc_irqflag & 3,
(m->mpc_irqflag >> 2) & 3, m->mpc_srcbus,
m->mpc_srcbusirq, m->mpc_dstapic, m->mpc_dstirq);
if (++mp_irq_entries == MAX_IRQ_SOURCES)
panic("Max # of irq sources exceeded!!\n");
}
static void __init MP_lintsrc_info (struct mpc_config_lintsrc *m)
{
Dprintk("Lint: type %d, pol %d, trig %d, bus %d,"
" IRQ %02x, APIC ID %x, APIC LINT %02x\n",
m->mpc_irqtype, m->mpc_irqflag & 3,
(m->mpc_irqflag >> 2) &3, m->mpc_srcbusid,
m->mpc_srcbusirq, m->mpc_destapic, m->mpc_destapiclint);
/*
* Well it seems all SMP boards in existence
* use ExtINT/LVT1 == LINT0 and
* NMI/LVT2 == LINT1 - the following check
* will show us if this assumptions is false.
* Until then we do not have to add baggage.
*/
if ((m->mpc_irqtype == mp_ExtINT) &&
(m->mpc_destapiclint != 0))
BUG();
if ((m->mpc_irqtype == mp_NMI) &&
(m->mpc_destapiclint != 1))
BUG();
}
/*
* Read/parse the MPC
*/
static int __init smp_read_mpc(struct mp_config_table *mpc)
{
char str[16];
int count=sizeof(*mpc);
unsigned char *mpt=((unsigned char *)mpc)+count;
if (memcmp(mpc->mpc_signature,MPC_SIGNATURE,4)) {
panic("SMP mptable: bad signature [%c%c%c%c]!\n",
mpc->mpc_signature[0],
mpc->mpc_signature[1],
mpc->mpc_signature[2],
mpc->mpc_signature[3]);
return 0;
}
if (mpf_checksum((unsigned char *)mpc,mpc->mpc_length)) {
panic("SMP mptable: checksum error!\n");
return 0;
}
if (mpc->mpc_spec!=0x01 && mpc->mpc_spec!=0x04) {
printk("SMP mptable: bad table version (%d)!!\n",
mpc->mpc_spec);
return 0;
}
if (!mpc->mpc_lapic) {
printk("SMP mptable: null local APIC address!\n");
return 0;
}
memcpy(str,mpc->mpc_oem,8);
str[8]=0;
printk("OEM ID: %s ",str);
memcpy(str,mpc->mpc_productid,12);
str[12]=0;
printk("Product ID: %s ",str);
printk("APIC at: 0x%lX\n", mpc->mpc_lapic);
/* save the local APIC address, it might be non-default. */
mp_lapic_addr = mpc->mpc_lapic;
/*
* Now process the configuration blocks.
*/
while (count < mpc->mpc_length) {
switch(*mpt) {
case MP_PROCESSOR:
{
struct mpc_config_processor *m=
(struct mpc_config_processor *)mpt;
MP_processor_info(m);
mpt += sizeof(*m);
count += sizeof(*m);
break;
}
case MP_BUS:
{
struct mpc_config_bus *m=
(struct mpc_config_bus *)mpt;
MP_bus_info(m);
mpt += sizeof(*m);
count += sizeof(*m);
break;
}
case MP_IOAPIC:
{
struct mpc_config_ioapic *m=
(struct mpc_config_ioapic *)mpt;
MP_ioapic_info(m);
mpt+=sizeof(*m);
count+=sizeof(*m);
break;
}
case MP_INTSRC:
{
struct mpc_config_intsrc *m=
(struct mpc_config_intsrc *)mpt;
MP_intsrc_info(m);
mpt+=sizeof(*m);
count+=sizeof(*m);
break;
}
case MP_LINTSRC:
{
struct mpc_config_lintsrc *m=
(struct mpc_config_lintsrc *)mpt;
MP_lintsrc_info(m);
mpt+=sizeof(*m);
count+=sizeof(*m);
break;
}
default:
{
count = mpc->mpc_length;
break;
}
}
++mpc_record;
}
if (!num_processors)
printk("SMP mptable: no processors registered!\n");
return num_processors;
}
static int __init ELCR_trigger(unsigned int irq)
{
unsigned int port;
port = 0x4d0 + (irq >> 3);
return (inb(port) >> (irq & 7)) & 1;
}
static void __init construct_default_ioirq_mptable(int mpc_default_type)
{
struct mpc_config_intsrc intsrc;
int i;
int ELCR_fallback = 0;
intsrc.mpc_type = MP_INTSRC;
intsrc.mpc_irqflag = 0; /* conforming */
intsrc.mpc_srcbus = 0;
intsrc.mpc_dstapic = mp_ioapics[0].mpc_apicid;
intsrc.mpc_irqtype = mp_INT;
/*
* If true, we have an ISA/PCI system with no IRQ entries
* in the MP table. To prevent the PCI interrupts from being set up
* incorrectly, we try to use the ELCR. The sanity check to see if
* there is good ELCR data is very simple - IRQ0, 1, 2 and 13 can
* never be level sensitive, so we simply see if the ELCR agrees.
* If it does, we assume it's valid.
*/
if (mpc_default_type == 5) {
printk("ISA/PCI bus type with no IRQ information... falling back to ELCR\n");
if (ELCR_trigger(0) || ELCR_trigger(1) || ELCR_trigger(2) || ELCR_trigger(13))
printk("ELCR contains invalid data... not using ELCR\n");
else {
printk("Using ELCR to identify PCI interrupts\n");
ELCR_fallback = 1;
}
}
for (i = 0; i < 16; i++) {
switch (mpc_default_type) {
case 2:
if (i == 0 || i == 13)
continue; /* IRQ0 & IRQ13 not connected */
/* fall through */
default:
if (i == 2)
continue; /* IRQ2 is never connected */
}
if (ELCR_fallback) {
/*
* If the ELCR indicates a level-sensitive interrupt, we
* copy that information over to the MP table in the
* irqflag field (level sensitive, active high polarity).
*/
if (ELCR_trigger(i))
intsrc.mpc_irqflag = 13;
else
intsrc.mpc_irqflag = 0;
}
intsrc.mpc_srcbusirq = i;
intsrc.mpc_dstirq = i ? i : 2; /* IRQ0 to INTIN2 */
MP_intsrc_info(&intsrc);
}
intsrc.mpc_irqtype = mp_ExtINT;
intsrc.mpc_srcbusirq = 0;
intsrc.mpc_dstirq = 0; /* 8259A to INTIN0 */
MP_intsrc_info(&intsrc);
}
static inline void __init construct_default_ISA_mptable(int mpc_default_type)
{
struct mpc_config_processor processor;
struct mpc_config_bus bus;
struct mpc_config_ioapic ioapic;
struct mpc_config_lintsrc lintsrc;
int linttypes[2] = { mp_ExtINT, mp_NMI };
int i;
/*
* local APIC has default address
*/
mp_lapic_addr = APIC_DEFAULT_PHYS_BASE;
/*
* 2 CPUs, numbered 0 & 1.
*/
processor.mpc_type = MP_PROCESSOR;
/* Either an integrated APIC or a discrete 82489DX. */
processor.mpc_apicver = mpc_default_type > 4 ? 0x10 : 0x01;
processor.mpc_cpuflag = CPU_ENABLED;
processor.mpc_cpufeature = (boot_cpu_data.x86 << 8) |
(boot_cpu_data.x86_model << 4) |
boot_cpu_data.x86_mask;
processor.mpc_featureflag = boot_cpu_data.x86_capability[0];
processor.mpc_reserved[0] = 0;
processor.mpc_reserved[1] = 0;
for (i = 0; i < 2; i++) {
processor.mpc_apicid = i;
MP_processor_info(&processor);
}
bus.mpc_type = MP_BUS;
bus.mpc_busid = 0;
switch (mpc_default_type) {
default:
printk("???\nUnknown standard configuration %d\n",
mpc_default_type);
/* fall through */
case 1:
case 5:
memcpy(bus.mpc_bustype, "ISA ", 6);
break;
case 2:
case 6:
case 3:
memcpy(bus.mpc_bustype, "EISA ", 6);
break;
case 4:
case 7:
memcpy(bus.mpc_bustype, "MCA ", 6);
}
MP_bus_info(&bus);
if (mpc_default_type > 4) {
bus.mpc_busid = 1;
memcpy(bus.mpc_bustype, "PCI ", 6);
MP_bus_info(&bus);
}
ioapic.mpc_type = MP_IOAPIC;
ioapic.mpc_apicid = 2;
ioapic.mpc_apicver = mpc_default_type > 4 ? 0x10 : 0x01;
ioapic.mpc_flags = MPC_APIC_USABLE;
ioapic.mpc_apicaddr = 0xFEC00000;
MP_ioapic_info(&ioapic);
/*
* We set up most of the low 16 IO-APIC pins according to MPS rules.
*/
construct_default_ioirq_mptable(mpc_default_type);
lintsrc.mpc_type = MP_LINTSRC;
lintsrc.mpc_irqflag = 0; /* conforming */
lintsrc.mpc_srcbusid = 0;
lintsrc.mpc_srcbusirq = 0;
lintsrc.mpc_destapic = MP_APIC_ALL;
for (i = 0; i < 2; i++) {
lintsrc.mpc_irqtype = linttypes[i];
lintsrc.mpc_destapiclint = i;
MP_lintsrc_info(&lintsrc);
}
}
static struct intel_mp_floating *mpf_found;
/*
* Scan the memory blocks for an SMP configuration block.
*/
void __init get_smp_config (void)
{
struct intel_mp_floating *mpf = mpf_found;
printk("Intel MultiProcessor Specification v1.%d\n", mpf->mpf_specification);
if (mpf->mpf_feature2 & (1<<7)) {
printk(" IMCR and PIC compatibility mode.\n");
pic_mode = 1;
} else {
printk(" Virtual Wire compatibility mode.\n");
pic_mode = 0;
}
/*
* Now see if we need to read further.
*/
if (mpf->mpf_feature1 != 0) {
printk("Default MP configuration #%d\n", mpf->mpf_feature1);
construct_default_ISA_mptable(mpf->mpf_feature1);
} else if (mpf->mpf_physptr) {
/*
* Read the physical hardware table. Anything here will
* override the defaults.
*/
if (!smp_read_mpc((void *)mpf->mpf_physptr)) {
smp_found_config = 0;
printk("BIOS bug, MP table errors detected!...\n");
printk("... disabling SMP support. (tell your hw vendor)\n");
return;
}
/*
* If there are no explicit MP IRQ entries, then we are
* broken. We set up most of the low 16 IO-APIC pins to
* ISA defaults and hope it will work.
*/
if (!mp_irq_entries) {
struct mpc_config_bus bus;
printk("BIOS bug, no explicit IRQ entries, using default mptable. (tell your hw vendor)\n");
bus.mpc_type = MP_BUS;
bus.mpc_busid = 0;
memcpy(bus.mpc_bustype, "ISA ", 6);
MP_bus_info(&bus);
construct_default_ioirq_mptable(0);
}
} else
BUG();
printk("Processors: %d\n", num_processors);
/*
* Only use the first configuration found.
*/
}
static int __init smp_scan_config (unsigned long base, unsigned long length)
{
unsigned long *bp = phys_to_virt(base);
struct intel_mp_floating *mpf;
Dprintk("Scan SMP from %p for %ld bytes.\n", bp,length);
if (sizeof(*mpf) != 16)
printk("Error: MPF size\n");
while (length > 0) {
mpf = (struct intel_mp_floating *)bp;
if ((*bp == SMP_MAGIC_IDENT) &&
(mpf->mpf_length == 1) &&
!mpf_checksum((unsigned char *)bp, 16) &&
((mpf->mpf_specification == 1)
|| (mpf->mpf_specification == 4)) ) {
smp_found_config = 1;
printk("found SMP MP-table at %08lx\n",
virt_to_phys(mpf));
reserve_bootmem(virt_to_phys(mpf), PAGE_SIZE);
if (mpf->mpf_physptr)
reserve_bootmem(mpf->mpf_physptr, PAGE_SIZE);
mpf_found = mpf;
return 1;
}
bp += 4;
length -= 16;
}
return 0;
}
void __init find_intel_smp (void)
{
/*
* 1) Scan the bottom 1K for a signature
* 2) Scan the top 1K of base RAM
* 3) Scan the 64K of bios
*/
if (smp_scan_config(0x0,0x400) ||
smp_scan_config(639*0x400,0x400) ||
smp_scan_config(0xF0000,0x10000))
return;
}
/*
* - Intel MP Configuration Table
* - or SGI Visual Workstation configuration
*/
void __init find_smp_config (void)
{
find_intel_smp();
}
+37
View File
@@ -0,0 +1,37 @@
/*
* Dynamic DMA mapping support.
*
* On i386 there is no hardware dynamic DMA address translation,
* so consistent alloc/free are merely page allocation/freeing.
* The rest of the dynamic DMA mapping interface is implemented
* in asm/pci.h.
*/
#include <linux/types.h>
#include <linux/mm.h>
#include <linux/lib.h>
#include <linux/pci.h>
#include <asm/io.h>
void *pci_alloc_consistent(struct pci_dev *hwdev, size_t size,
dma_addr_t *dma_handle)
{
void *ret;
int gfp = GFP_ATOMIC;
if (hwdev == NULL || hwdev->dma_mask != 0xffffffff)
gfp |= GFP_DMA;
ret = (void *)__get_free_pages(gfp, get_order(size));
if (ret != NULL) {
memset(ret, 0, size);
*dma_handle = virt_to_bus(ret);
}
return ret;
}
void pci_free_consistent(struct pci_dev *hwdev, size_t size,
void *vaddr, dma_addr_t dma_handle)
{
free_pages((unsigned long)vaddr, get_order(size));
}
+354
View File
@@ -0,0 +1,354 @@
/*
* Low-Level PCI Access for i386 machines
*
* Copyright 1993, 1994 Drew Eckhardt
* Visionary Computing
* (Unix and Linux consulting and custom programming)
* Drew@Colorado.EDU
* +1 (303) 786-7975
*
* Drew's work was sponsored by:
* iX Multiuser Multitasking Magazine
* Hannover, Germany
* hm@ix.de
*
* Copyright 1997--2000 Martin Mares <mj@ucw.cz>
*
* For more information, please consult the following manuals (look at
* http://www.pcisig.com/ for how to get them):
*
* PCI BIOS Specification
* PCI Local Bus Specification
* PCI to PCI Bridge Specification
* PCI System Design Guide
*
*
* CHANGELOG :
* Jun 17, 1994 : Modified to accommodate the broken pre-PCI BIOS SPECIFICATION
* Revision 2.0 present on <thys@dennis.ee.up.ac.za>'s ASUS mainboard.
*
* Jan 5, 1995 : Modified to probe PCI hardware at boot time by Frederic
* Potter, potter@cao-vlsi.ibp.fr
*
* Jan 10, 1995 : Modified to store the information about configured pci
* devices into a list, which can be accessed via /proc/pci by
* Curtis Varner, cvarner@cs.ucr.edu
*
* Jan 12, 1995 : CPU-PCI bridge optimization support by Frederic Potter.
* Alpha version. Intel & UMC chipset support only.
*
* Apr 16, 1995 : Source merge with the DEC Alpha PCI support. Most of the code
* moved to drivers/pci/pci.c.
*
* Dec 7, 1996 : Added support for direct configuration access of boards
* with Intel compatible access schemes (tsbogend@alpha.franken.de)
*
* Feb 3, 1997 : Set internal functions to static, save/restore flags
* avoid dead locks reading broken PCI BIOS, werner@suse.de
*
* Apr 26, 1997 : Fixed case when there is BIOS32, but not PCI BIOS
* (mj@atrey.karlin.mff.cuni.cz)
*
* May 7, 1997 : Added some missing cli()'s. [mj]
*
* Jun 20, 1997 : Corrected problems in "conf1" type accesses.
* (paubert@iram.es)
*
* Aug 2, 1997 : Split to PCI BIOS handling and direct PCI access parts
* and cleaned it up... Martin Mares <mj@atrey.karlin.mff.cuni.cz>
*
* Feb 6, 1998 : No longer using BIOS to find devices and device classes. [mj]
*
* May 1, 1998 : Support for peer host bridges. [mj]
*
* Jun 19, 1998 : Changed to use spinlocks, so that PCI configuration space
* can be accessed from interrupts even on SMP systems. [mj]
*
* August 1998 : Better support for peer host bridges and more paranoid
* checks for direct hardware access. Ugh, this file starts to look as
* a large gallery of common hardware bug workarounds (watch the comments)
* -- the PCI specs themselves are sane, but most implementors should be
* hit hard with \hammer scaled \magstep5. [mj]
*
* Jan 23, 1999 : More improvements to peer host bridge logic. i450NX fixup. [mj]
*
* Feb 8, 1999 : Added UM8886BF I/O address fixup. [mj]
*
* August 1999 : New resource management and configuration access stuff. [mj]
*
* Sep 19, 1999 : Use PCI IRQ routing tables for detection of peer host bridges.
* Based on ideas by Chris Frantz and David Hinds. [mj]
*
* Sep 28, 1999 : Handle unreported/unassigned IRQs. Thanks to Shuu Yamaguchi
* for a lot of patience during testing. [mj]
*
* Oct 8, 1999 : Split to pci-i386.c, pci-pc.c and pci-visws.c. [mj]
*/
#include <linux/types.h>
//#include <linux/kernel.h>
#include <linux/pci.h>
#include <linux/init.h>
#include <linux/ioport.h>
#include <linux/errno.h>
#include "pci-i386.h"
void
pcibios_update_resource(struct pci_dev *dev, struct resource *root,
struct resource *res, int resource)
{
u32 new, check;
int reg;
new = res->start | (res->flags & PCI_REGION_FLAG_MASK);
if (resource < 6) {
reg = PCI_BASE_ADDRESS_0 + 4*resource;
} else if (resource == PCI_ROM_RESOURCE) {
res->flags |= PCI_ROM_ADDRESS_ENABLE;
new |= PCI_ROM_ADDRESS_ENABLE;
reg = dev->rom_base_reg;
} else {
/* Somebody might have asked allocation of a non-standard resource */
return;
}
pci_write_config_dword(dev, reg, new);
pci_read_config_dword(dev, reg, &check);
if ((new ^ check) & ((new & PCI_BASE_ADDRESS_SPACE_IO) ? PCI_BASE_ADDRESS_IO_MASK : PCI_BASE_ADDRESS_MEM_MASK)) {
printk(KERN_ERR "PCI: Error while updating region "
"%s/%d (%08x != %08x)\n", dev->slot_name, resource,
new, check);
}
}
/*
* We need to avoid collisions with `mirrored' VGA ports
* and other strange ISA hardware, so we always want the
* addresses to be allocated in the 0x000-0x0ff region
* modulo 0x400.
*
* Why? Because some silly external IO cards only decode
* the low 10 bits of the IO address. The 0x00-0xff region
* is reserved for motherboard devices that decode all 16
* bits, so it's ok to allocate at, say, 0x2800-0x28ff,
* but we want to try to avoid allocating at 0x2900-0x2bff
* which might have be mirrored at 0x0100-0x03ff..
*/
void
pcibios_align_resource(void *data, struct resource *res, unsigned long size)
{
if (res->flags & IORESOURCE_IO) {
unsigned long start = res->start;
if (start & 0x300) {
start = (start + 0x3ff) & ~0x3ff;
res->start = start;
}
}
}
/*
* Handle resources of PCI devices. If the world were perfect, we could
* just allocate all the resource regions and do nothing more. It isn't.
* On the other hand, we cannot just re-allocate all devices, as it would
* require us to know lots of host bridge internals. So we attempt to
* keep as much of the original configuration as possible, but tweak it
* when it's found to be wrong.
*
* Known BIOS problems we have to work around:
* - I/O or memory regions not configured
* - regions configured, but not enabled in the command register
* - bogus I/O addresses above 64K used
* - expansion ROMs left enabled (this may sound harmless, but given
* the fact the PCI specs explicitly allow address decoders to be
* shared between expansion ROMs and other resource regions, it's
* at least dangerous)
*
* Our solution:
* (1) Allocate resources for all buses behind PCI-to-PCI bridges.
* This gives us fixed barriers on where we can allocate.
* (2) Allocate resources for all enabled devices. If there is
* a collision, just mark the resource as unallocated. Also
* disable expansion ROMs during this step.
* (3) Try to allocate resources for disabled devices. If the
* resources were assigned correctly, everything goes well,
* if they weren't, they won't disturb allocation of other
* resources.
* (4) Assign new addresses to resources which were either
* not configured at all or misconfigured. If explicitly
* requested by the user, configure expansion ROM address
* as well.
*/
static void __init pcibios_allocate_bus_resources(struct list_head *bus_list)
{
struct list_head *ln;
struct pci_bus *bus;
struct pci_dev *dev;
int idx;
struct resource *r, *pr;
/* Depth-First Search on bus tree */
for (ln=bus_list->next; ln != bus_list; ln=ln->next) {
bus = pci_bus_b(ln);
if ((dev = bus->self)) {
for (idx = PCI_BRIDGE_RESOURCES; idx < PCI_NUM_RESOURCES; idx++) {
r = &dev->resource[idx];
if (!r->start)
continue;
pr = pci_find_parent_resource(dev, r);
if (!pr || request_resource(pr, r) < 0)
printk(KERN_ERR "PCI: Cannot allocate resource region %d of bridge %s\n", idx, dev->slot_name);
}
}
pcibios_allocate_bus_resources(&bus->children);
}
}
static void __init pcibios_allocate_resources(int pass)
{
struct pci_dev *dev;
int idx, disabled;
u16 command;
struct resource *r, *pr;
pci_for_each_dev(dev) {
pci_read_config_word(dev, PCI_COMMAND, &command);
for(idx = 0; idx < 6; idx++) {
r = &dev->resource[idx];
if (r->parent) /* Already allocated */
continue;
if (!r->start) /* Address not assigned at all */
continue;
if (r->flags & IORESOURCE_IO)
disabled = !(command & PCI_COMMAND_IO);
else
disabled = !(command & PCI_COMMAND_MEMORY);
if (pass == disabled) {
DBG("PCI: Resource %08lx-%08lx (f=%lx, d=%d, p=%d)\n",
r->start, r->end, r->flags, disabled, pass);
pr = pci_find_parent_resource(dev, r);
if (!pr || request_resource(pr, r) < 0) {
printk(KERN_ERR "PCI: Cannot allocate resource region %d of device %s\n", idx, dev->slot_name);
/* We'll assign a new address later */
r->end -= r->start;
r->start = 0;
}
}
}
if (!pass) {
r = &dev->resource[PCI_ROM_RESOURCE];
if (r->flags & PCI_ROM_ADDRESS_ENABLE) {
/* Turn the ROM off, leave the resource region, but keep it unregistered. */
u32 reg;
DBG("PCI: Switching off ROM of %s\n", dev->slot_name);
r->flags &= ~PCI_ROM_ADDRESS_ENABLE;
pci_read_config_dword(dev, dev->rom_base_reg, &reg);
pci_write_config_dword(dev, dev->rom_base_reg, reg & ~PCI_ROM_ADDRESS_ENABLE);
}
}
}
}
static void __init pcibios_assign_resources(void)
{
struct pci_dev *dev;
int idx;
struct resource *r;
pci_for_each_dev(dev) {
int class = dev->class >> 8;
/* Don't touch classless devices and host bridges */
if (!class || class == PCI_CLASS_BRIDGE_HOST)
continue;
for(idx=0; idx<6; idx++) {
r = &dev->resource[idx];
/*
* Don't touch IDE controllers and I/O ports of video cards!
*/
if ((class == PCI_CLASS_STORAGE_IDE && idx < 4) ||
(class == PCI_CLASS_DISPLAY_VGA && (r->flags & IORESOURCE_IO)))
continue;
/*
* We shall assign a new address to this resource, either because
* the BIOS forgot to do so or because we have decided the old
* address was unusable for some reason.
*/
if (!r->start && r->end)
pci_assign_resource(dev, idx);
}
if (pci_probe & PCI_ASSIGN_ROMS) {
r = &dev->resource[PCI_ROM_RESOURCE];
r->end -= r->start;
r->start = 0;
if (r->end)
pci_assign_resource(dev, PCI_ROM_RESOURCE);
}
}
}
void __init pcibios_resource_survey(void)
{
DBG("PCI: Allocating resources\n");
pcibios_allocate_bus_resources(&pci_root_buses);
pcibios_allocate_resources(0);
pcibios_allocate_resources(1);
pcibios_assign_resources();
}
int pcibios_enable_resources(struct pci_dev *dev)
{
u16 cmd, old_cmd;
int idx;
struct resource *r;
pci_read_config_word(dev, PCI_COMMAND, &cmd);
old_cmd = cmd;
for(idx=0; idx<6; idx++) {
r = &dev->resource[idx];
if (!r->start && r->end) {
printk(KERN_ERR "PCI: Device %s not available because of resource collisions\n", dev->slot_name);
return -EINVAL;
}
if (r->flags & IORESOURCE_IO)
cmd |= PCI_COMMAND_IO;
if (r->flags & IORESOURCE_MEM)
cmd |= PCI_COMMAND_MEMORY;
}
if (dev->resource[PCI_ROM_RESOURCE].start)
cmd |= PCI_COMMAND_MEMORY;
if (cmd != old_cmd) {
printk("PCI: Enabling device %s (%04x -> %04x)\n", dev->slot_name, old_cmd, cmd);
pci_write_config_word(dev, PCI_COMMAND, cmd);
}
return 0;
}
/*
* If we set up a device for bus mastering, we need to check the latency
* timer as certain crappy BIOSes forget to set it properly.
*/
unsigned int pcibios_max_latency = 255;
void pcibios_set_master(struct pci_dev *dev)
{
u8 lat;
pci_read_config_byte(dev, PCI_LATENCY_TIMER, &lat);
if (lat < 16)
lat = (64 <= pcibios_max_latency) ? 64 : pcibios_max_latency;
else if (lat > pcibios_max_latency)
lat = pcibios_max_latency;
else
return;
printk("PCI: Setting latency timer of device %s to %d\n", dev->slot_name, lat);
pci_write_config_byte(dev, PCI_LATENCY_TIMER, lat);
}
+69
View File
@@ -0,0 +1,69 @@
/*
* Low-Level PCI Access for i386 machines.
*
* (c) 1999 Martin Mares <mj@ucw.cz>
*/
#undef DEBUG
#ifdef DEBUG
#define DBG(x...) printk(x)
#else
#define DBG(x...)
#endif
#define PCI_PROBE_BIOS 0x0001
#define PCI_PROBE_CONF1 0x0002
#define PCI_PROBE_CONF2 0x0004
#define PCI_NO_SORT 0x0100
#define PCI_BIOS_SORT 0x0200
#define PCI_NO_CHECKS 0x0400
#define PCI_ASSIGN_ROMS 0x1000
#define PCI_BIOS_IRQ_SCAN 0x2000
#define PCI_ASSIGN_ALL_BUSSES 0x4000
extern unsigned int pci_probe;
/* pci-i386.c */
extern unsigned int pcibios_max_latency;
void pcibios_resource_survey(void);
int pcibios_enable_resources(struct pci_dev *);
/* pci-pc.c */
extern int pcibios_last_bus;
extern struct pci_bus *pci_root_bus;
extern struct pci_ops *pci_root_ops;
/* pci-irq.c */
struct irq_info {
u8 bus, devfn; /* Bus, device and function */
struct {
u8 link; /* IRQ line ID, chipset dependent, 0=not routed */
u16 bitmap; /* Available IRQs */
} __attribute__((packed)) irq[4];
u8 slot; /* Slot number, 0=onboard */
u8 rfu;
} __attribute__((packed));
struct irq_routing_table {
u32 signature; /* PIRQ_SIGNATURE should be here */
u16 version; /* PIRQ_VERSION */
u16 size; /* Table size in bytes */
u8 rtr_bus, rtr_devfn; /* Where the interrupt router lies */
u16 exclusive_irqs; /* IRQs devoted exclusively to PCI usage */
u16 rtr_vendor, rtr_device; /* Vendor and device ID of interrupt router */
u32 miniport_data; /* Crap */
u8 rfu[11];
u8 checksum; /* Modulo 256 checksum must give zero */
struct irq_info slots[0];
} __attribute__((packed));
extern unsigned int pcibios_irq_mask;
void pcibios_irq_init(void);
void pcibios_fixup_irqs(void);
void pcibios_enable_irq(struct pci_dev *dev);
+753
View File
@@ -0,0 +1,753 @@
/*
* Low-Level PCI Support for PC -- Routing of Interrupts
*
* (c) 1999--2000 Martin Mares <mj@ucw.cz>
*/
#include <linux/config.h>
#include <linux/types.h>
//#include <linux/kernel.h>
#include <linux/pci.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/sched.h>
#include <asm/io.h>
#include <asm/smp.h>
#include <asm/io_apic.h>
#include "pci-i386.h"
#define PIRQ_SIGNATURE (('$' << 0) + ('P' << 8) + ('I' << 16) + ('R' << 24))
#define PIRQ_VERSION 0x0100
static struct irq_routing_table *pirq_table;
/*
* Never use: 0, 1, 2 (timer, keyboard, and cascade)
* Avoid using: 13, 14 and 15 (FP error and IDE).
* Penalize: 3, 4, 6, 7, 12 (known ISA uses: serial, floppy, parallel and mouse)
*/
unsigned int pcibios_irq_mask = 0xfff8;
static int pirq_penalty[16] = {
1000000, 1000000, 1000000, 1000, 1000, 0, 1000, 1000,
0, 0, 0, 0, 1000, 100000, 100000, 100000
};
struct irq_router {
char *name;
u16 vendor, device;
int (*get)(struct pci_dev *router, struct pci_dev *dev, int pirq);
int (*set)(struct pci_dev *router, struct pci_dev *dev, int pirq, int new);
};
/*
* Search 0xf0000 -- 0xfffff for the PCI IRQ Routing Table.
*/
static struct irq_routing_table * __init pirq_find_routing_table(void)
{
u8 *addr;
struct irq_routing_table *rt;
int i;
u8 sum;
for(addr = (u8 *) __va(0xf0000); addr < (u8 *) __va(0x100000); addr += 16) {
rt = (struct irq_routing_table *) addr;
if (rt->signature != PIRQ_SIGNATURE ||
rt->version != PIRQ_VERSION ||
rt->size % 16 ||
rt->size < sizeof(struct irq_routing_table))
continue;
sum = 0;
for(i=0; i<rt->size; i++)
sum += addr[i];
if (!sum) {
DBG("PCI: Interrupt Routing Table found at 0x%p\n", rt);
return rt;
}
}
return NULL;
}
/*
* If we have a IRQ routing table, use it to search for peer host
* bridges. It's a gross hack, but since there are no other known
* ways how to get a list of buses, we have to go this way.
*/
static void __init pirq_peer_trick(void)
{
struct irq_routing_table *rt = pirq_table;
u8 busmap[256];
int i;
struct irq_info *e;
memset(busmap, 0, sizeof(busmap));
for(i=0; i < (rt->size - sizeof(struct irq_routing_table)) / sizeof(struct irq_info); i++) {
e = &rt->slots[i];
#ifdef DEBUG
{
int j;
DBG("%02x:%02x slot=%02x", e->bus, e->devfn/8, e->slot);
for(j=0; j<4; j++)
DBG(" %d:%02x/%04x", j, e->irq[j].link, e->irq[j].bitmap);
DBG("\n");
}
#endif
busmap[e->bus] = 1;
}
for(i=1; i<256; i++)
/*
* It might be a secondary bus, but in this case its parent is already
* known (ascending bus order) and therefore pci_scan_bus returns immediately.
*/
if (busmap[i] && pci_scan_bus(i, pci_root_bus->ops, NULL))
printk(KERN_INFO "PCI: Discovered primary peer bus %02x [IRQ]\n", i);
pcibios_last_bus = -1;
}
/*
* Code for querying and setting of IRQ routes on various interrupt routers.
*/
static void eisa_set_level_irq(unsigned int irq)
{
unsigned char mask = 1 << (irq & 7);
unsigned int port = 0x4d0 + (irq >> 3);
unsigned char val = inb(port);
if (!(val & mask)) {
DBG(" -> edge");
outb(val | mask, port);
}
}
/*
* Common IRQ routing practice: nybbles in config space,
* offset by some magic constant.
*/
static unsigned int read_config_nybble(struct pci_dev *router, unsigned offset, unsigned nr)
{
u8 x;
unsigned reg = offset + (nr >> 1);
pci_read_config_byte(router, reg, &x);
return (nr & 1) ? (x >> 4) : (x & 0xf);
}
static void write_config_nybble(struct pci_dev *router, unsigned offset, unsigned nr, unsigned int val)
{
u8 x;
unsigned reg = offset + (nr >> 1);
pci_read_config_byte(router, reg, &x);
x = (nr & 1) ? ((x & 0x0f) | (val << 4)) : ((x & 0xf0) | val);
pci_write_config_byte(router, reg, x);
}
/*
* ALI pirq entries are damn ugly, and completely undocumented.
* This has been figured out from pirq tables, and it's not a pretty
* picture.
*/
static int pirq_ali_get(struct pci_dev *router, struct pci_dev *dev, int pirq)
{
static unsigned char irqmap[16] = { 0, 9, 3, 10, 4, 5, 7, 6, 1, 11, 0, 12, 0, 14, 0, 15 };
return irqmap[read_config_nybble(router, 0x48, pirq-1)];
}
static int pirq_ali_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq)
{
static unsigned char irqmap[16] = { 0, 8, 0, 2, 4, 5, 7, 6, 0, 1, 3, 9, 11, 0, 13, 15 };
unsigned int val = irqmap[irq];
if (val) {
write_config_nybble(router, 0x48, pirq-1, val);
return 1;
}
return 0;
}
/*
* The Intel PIIX4 pirq rules are fairly simple: "pirq" is
* just a pointer to the config space.
*/
static int pirq_piix_get(struct pci_dev *router, struct pci_dev *dev, int pirq)
{
u8 x;
pci_read_config_byte(router, pirq, &x);
return (x < 16) ? x : 0;
}
static int pirq_piix_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq)
{
pci_write_config_byte(router, pirq, irq);
return 1;
}
/*
* The VIA pirq rules are nibble-based, like ALI,
* but without the ugly irq number munging.
*/
static int pirq_via_get(struct pci_dev *router, struct pci_dev *dev, int pirq)
{
return read_config_nybble(router, 0x55, pirq);
}
static int pirq_via_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq)
{
write_config_nybble(router, 0x55, pirq, irq);
return 1;
}
/*
* OPTI: high four bits are nibble pointer..
* I wonder what the low bits do?
*/
static int pirq_opti_get(struct pci_dev *router, struct pci_dev *dev, int pirq)
{
return read_config_nybble(router, 0xb8, pirq >> 4);
}
static int pirq_opti_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq)
{
write_config_nybble(router, 0xb8, pirq >> 4, irq);
return 1;
}
/*
* Cyrix: nibble offset 0x5C
*/
static int pirq_cyrix_get(struct pci_dev *router, struct pci_dev *dev, int pirq)
{
return read_config_nybble(router, 0x5C, pirq-1);
}
static int pirq_cyrix_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq)
{
write_config_nybble(router, 0x5C, pirq-1, irq);
return 1;
}
/*
* PIRQ routing for SiS 85C503 router used in several SiS chipsets
* According to the SiS 5595 datasheet (preliminary V1.0, 12/24/1997)
* the related registers work as follows:
*
* general: one byte per re-routable IRQ,
* bit 7 IRQ mapping enabled (0) or disabled (1)
* bits [6:4] reserved
* bits [3:0] IRQ to map to
* allowed: 3-7, 9-12, 14-15
* reserved: 0, 1, 2, 8, 13
*
* individual registers in device config space:
*
* 0x41/0x42/0x43/0x44: PCI INT A/B/C/D - bits as in general case
*
* 0x61: IDEIRQ: bits as in general case - but:
* bits [6:5] must be written 01
* bit 4 channel-select primary (0), secondary (1)
*
* 0x62: USBIRQ: bits as in general case - but:
* bit 4 OHCI function disabled (0), enabled (1)
*
* 0x6a: ACPI/SCI IRQ - bits as in general case
*
* 0x7e: Data Acq. Module IRQ - bits as in general case
*
* Apparently there are systems implementing PCI routing table using both
* link values 0x01-0x04 and 0x41-0x44 for PCI INTA..D, but register offsets
* like 0x62 as link values for USBIRQ e.g. So there is no simple
* "register = offset + pirq" relation.
* Currently we support PCI INTA..D and USBIRQ and try our best to handle
* both link mappings.
* IDE/ACPI/DAQ mapping is currently unsupported (left untouched as set by BIOS).
*/
static int pirq_sis_get(struct pci_dev *router, struct pci_dev *dev, int pirq)
{
u8 x;
int reg = pirq;
switch(pirq) {
case 0x01:
case 0x02:
case 0x03:
case 0x04:
reg += 0x40;
case 0x41:
case 0x42:
case 0x43:
case 0x44:
case 0x62:
pci_read_config_byte(router, reg, &x);
if (reg != 0x62)
break;
if (!(x & 0x40))
return 0;
break;
case 0x61:
case 0x6a:
case 0x7e:
printk(KERN_INFO "SiS pirq: advanced IDE/ACPI/DAQ mapping not yet implemented\n");
return 0;
default:
printk(KERN_INFO "SiS router pirq escape (%d)\n", pirq);
return 0;
}
return (x & 0x80) ? 0 : (x & 0x0f);
}
static int pirq_sis_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq)
{
u8 x;
int reg = pirq;
switch(pirq) {
case 0x01:
case 0x02:
case 0x03:
case 0x04:
reg += 0x40;
case 0x41:
case 0x42:
case 0x43:
case 0x44:
case 0x62:
x = (irq&0x0f) ? (irq&0x0f) : 0x80;
if (reg != 0x62)
break;
/* always mark OHCI enabled, as nothing else knows about this */
x |= 0x40;
break;
case 0x61:
case 0x6a:
case 0x7e:
printk(KERN_INFO "advanced SiS pirq mapping not yet implemented\n");
return 0;
default:
printk(KERN_INFO "SiS router pirq escape (%d)\n", pirq);
return 0;
}
pci_write_config_byte(router, reg, x);
return 1;
}
/*
* VLSI: nibble offset 0x74 - educated guess due to routing table and
* config space of VLSI 82C534 PCI-bridge/router (1004:0102)
* Tested on HP OmniBook 800 covering PIRQ 1, 2, 4, 8 for onboard
* devices, PIRQ 3 for non-pci(!) soundchip and (untested) PIRQ 6
* for the busbridge to the docking station.
*/
static int pirq_vlsi_get(struct pci_dev *router, struct pci_dev *dev, int pirq)
{
if (pirq > 8) {
printk(KERN_INFO "VLSI router pirq escape (%d)\n", pirq);
return 0;
}
return read_config_nybble(router, 0x74, pirq-1);
}
static int pirq_vlsi_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq)
{
if (pirq > 8) {
printk(KERN_INFO "VLSI router pirq escape (%d)\n", pirq);
return 0;
}
write_config_nybble(router, 0x74, pirq-1, irq);
return 1;
}
/*
* ServerWorks: PCI interrupts mapped to system IRQ lines through Index
* and Redirect I/O registers (0x0c00 and 0x0c01). The Index register
* format is (PCIIRQ## | 0x10), e.g.: PCIIRQ10=0x1a. The Redirect
* register is a straight binary coding of desired PIC IRQ (low nibble).
*
* The 'link' value in the PIRQ table is already in the correct format
* for the Index register. There are some special index values:
* 0x00 for ACPI (SCI), 0x01 for USB, 0x02 for IDE0, 0x04 for IDE1,
* and 0x03 for SMBus.
*/
static int pirq_serverworks_get(struct pci_dev *router, struct pci_dev *dev, int pirq)
{
outb_p(pirq, 0xc00);
return inb(0xc01) & 0xf;
}
static int pirq_serverworks_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq)
{
outb_p(pirq, 0xc00);
outb_p(irq, 0xc01);
return 1;
}
/* Support for AMD756 PCI IRQ Routing
* Jhon H. Caicedo <jhcaiced@osso.org.co>
* Jun/21/2001 0.2.0 Release, fixed to use "nybble" functions... (jhcaiced)
* Jun/19/2001 Alpha Release 0.1.0 (jhcaiced)
* The AMD756 pirq rules are nibble-based
* offset 0x56 0-3 PIRQA 4-7 PIRQB
* offset 0x57 0-3 PIRQC 4-7 PIRQD
*/
static int pirq_amd756_get(struct pci_dev *router, struct pci_dev *dev, int pirq)
{
u8 irq;
irq = 0;
if (pirq <= 4)
{
irq = read_config_nybble(router, 0x56, pirq - 1);
}
printk(KERN_INFO "AMD756: dev %04x:%04x, router pirq : %d get irq : %2d\n",
dev->vendor, dev->device, pirq, irq);
return irq;
}
static int pirq_amd756_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq)
{
printk(KERN_INFO "AMD756: dev %04x:%04x, router pirq : %d SET irq : %2d\n",
dev->vendor, dev->device, pirq, irq);
if (pirq <= 4)
{
write_config_nybble(router, 0x56, pirq - 1, irq);
}
return 1;
}
#ifdef CONFIG_PCI_BIOS
static int pirq_bios_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq)
{
struct pci_dev *bridge;
int pin = pci_get_interrupt_pin(dev, &bridge);
return pcibios_set_irq_routing(bridge, pin, irq);
}
static struct irq_router pirq_bios_router =
{ "BIOS", 0, 0, NULL, pirq_bios_set };
#endif
static struct irq_router pirq_routers[] = {
{ "PIIX", PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371FB_0, pirq_piix_get, pirq_piix_set },
{ "PIIX", PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371SB_0, pirq_piix_get, pirq_piix_set },
{ "PIIX", PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_0, pirq_piix_get, pirq_piix_set },
{ "PIIX", PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371MX, pirq_piix_get, pirq_piix_set },
{ "PIIX", PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82443MX_0, pirq_piix_get, pirq_piix_set },
{ "PIIX", PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_0, pirq_piix_get, pirq_piix_set },
{ "ALI", PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M1533, pirq_ali_get, pirq_ali_set },
{ "VIA", PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C586_0, pirq_via_get, pirq_via_set },
{ "VIA", PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C596, pirq_via_get, pirq_via_set },
{ "VIA", PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C686, pirq_via_get, pirq_via_set },
{ "OPTI", PCI_VENDOR_ID_OPTI, PCI_DEVICE_ID_OPTI_82C700, pirq_opti_get, pirq_opti_set },
{ "NatSemi", PCI_VENDOR_ID_CYRIX, PCI_DEVICE_ID_CYRIX_5520, pirq_cyrix_get, pirq_cyrix_set },
{ "SIS", PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_503, pirq_sis_get, pirq_sis_set },
{ "VLSI 82C534", PCI_VENDOR_ID_VLSI, PCI_DEVICE_ID_VLSI_82C534, pirq_vlsi_get, pirq_vlsi_set },
{ "ServerWorks", PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_OSB4,
pirq_serverworks_get, pirq_serverworks_set },
{ "ServerWorks", PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_CSB5,
pirq_serverworks_get, pirq_serverworks_set },
{ "AMD756 VIPER", PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_VIPER_740B,
pirq_amd756_get, pirq_amd756_set },
{ "default", 0, 0, NULL, NULL }
};
static struct irq_router *pirq_router;
static struct pci_dev *pirq_router_dev;
static void __init pirq_find_router(void)
{
struct irq_routing_table *rt = pirq_table;
struct irq_router *r;
#ifdef CONFIG_PCI_BIOS
if (!rt->signature) {
printk(KERN_INFO "PCI: Using BIOS for IRQ routing\n");
pirq_router = &pirq_bios_router;
return;
}
#endif
DBG("PCI: Attempting to find IRQ router for %04x:%04x\n",
rt->rtr_vendor, rt->rtr_device);
/* fall back to default router if nothing else found */
pirq_router = &pirq_routers[ARRAY_SIZE(pirq_routers) - 1];
pirq_router_dev = pci_find_slot(rt->rtr_bus, rt->rtr_devfn);
if (!pirq_router_dev) {
DBG("PCI: Interrupt router not found at %02x:%02x\n", rt->rtr_bus, rt->rtr_devfn);
return;
}
for(r=pirq_routers; r->vendor; r++) {
/* Exact match against router table entry? Use it! */
if (r->vendor == rt->rtr_vendor && r->device == rt->rtr_device) {
pirq_router = r;
break;
}
/* Match against router device entry? Use it as a fallback */
if (r->vendor == pirq_router_dev->vendor && r->device == pirq_router_dev->device) {
pirq_router = r;
}
}
printk(KERN_INFO "PCI: Using IRQ router %s [%04x/%04x] at %s\n",
pirq_router->name,
pirq_router_dev->vendor,
pirq_router_dev->device,
pirq_router_dev->slot_name);
}
static struct irq_info *pirq_get_info(struct pci_dev *dev)
{
struct irq_routing_table *rt = pirq_table;
int entries = (rt->size - sizeof(struct irq_routing_table)) / sizeof(struct irq_info);
struct irq_info *info;
for (info = rt->slots; entries--; info++)
if (info->bus == dev->bus->number && PCI_SLOT(info->devfn) == PCI_SLOT(dev->devfn))
return info;
return NULL;
}
static void pcibios_test_irq_handler(int irq, void *dev_id, struct pt_regs *regs)
{
}
static int pcibios_lookup_irq(struct pci_dev *dev, int assign)
{
u8 pin;
struct irq_info *info;
int i, pirq, newirq;
int irq = 0;
u32 mask;
struct irq_router *r = pirq_router;
struct pci_dev *dev2;
char *msg = NULL;
if (!pirq_table)
return 0;
/* Find IRQ routing entry */
pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin);
if (!pin) {
DBG(" -> no interrupt pin\n");
return 0;
}
pin = pin - 1;
DBG("IRQ for %s:%d", dev->slot_name, pin);
info = pirq_get_info(dev);
if (!info) {
DBG(" -> not found in routing table\n");
return 0;
}
pirq = info->irq[pin].link;
mask = info->irq[pin].bitmap;
if (!pirq) {
DBG(" -> not routed\n");
return 0;
}
DBG(" -> PIRQ %02x, mask %04x, excl %04x", pirq, mask, pirq_table->exclusive_irqs);
mask &= pcibios_irq_mask;
/*
* Find the best IRQ to assign: use the one
* reported by the device if possible.
*/
newirq = dev->irq;
if (!newirq && assign) {
for (i = 0; i < 16; i++) {
if (!(mask & (1 << i)))
continue;
if (pirq_penalty[i] < pirq_penalty[newirq] &&
!request_irq(i, pcibios_test_irq_handler, SA_SHIRQ, "pci-test", dev)) {
free_irq(i, dev);
newirq = i;
}
}
}
DBG(" -> newirq=%d", newirq);
/* Check if it is hardcoded */
if ((pirq & 0xf0) == 0xf0) {
irq = pirq & 0xf;
DBG(" -> hardcoded IRQ %d\n", irq);
msg = "Hardcoded";
} else if (r->get && (irq = r->get(pirq_router_dev, dev, pirq))) {
DBG(" -> got IRQ %d\n", irq);
msg = "Found";
} else if (newirq && r->set && (dev->class >> 8) != PCI_CLASS_DISPLAY_VGA) {
DBG(" -> assigning IRQ %d", newirq);
if (r->set(pirq_router_dev, dev, pirq, newirq)) {
eisa_set_level_irq(newirq);
DBG(" ... OK\n");
msg = "Assigned";
irq = newirq;
}
}
if (!irq) {
DBG(" ... failed\n");
if (newirq && mask == (1 << newirq)) {
msg = "Guessed";
irq = newirq;
} else
return 0;
}
printk(KERN_INFO "PCI: %s IRQ %d for device %s\n", msg, irq, dev->slot_name);
/* Update IRQ for all devices with the same pirq value */
pci_for_each_dev(dev2) {
pci_read_config_byte(dev2, PCI_INTERRUPT_PIN, &pin);
if (!pin)
continue;
pin--;
info = pirq_get_info(dev2);
if (!info)
continue;
if (info->irq[pin].link == pirq) {
/* We refuse to override the dev->irq information. Give a warning! */
if (dev2->irq && dev2->irq != irq) {
printk(KERN_INFO "IRQ routing conflict for %s, have irq %d, want irq %d\n",
dev2->slot_name, dev2->irq, irq);
continue;
}
dev2->irq = irq;
pirq_penalty[irq]++;
if (dev != dev2)
printk(KERN_INFO "PCI: Sharing IRQ %d with %s\n", irq, dev2->slot_name);
}
}
return 1;
}
void __init pcibios_irq_init(void)
{
DBG("PCI: IRQ init\n");
pirq_table = pirq_find_routing_table();
#ifdef CONFIG_PCI_BIOS
if (!pirq_table && (pci_probe & PCI_BIOS_IRQ_SCAN))
pirq_table = pcibios_get_irq_routing_table();
#endif
if (pirq_table) {
pirq_peer_trick();
pirq_find_router();
if (pirq_table->exclusive_irqs) {
int i;
for (i=0; i<16; i++)
if (!(pirq_table->exclusive_irqs & (1 << i)))
pirq_penalty[i] += 100;
}
/* If we're using the I/O APIC, avoid using the PCI IRQ routing table */
if (io_apic_assign_pci_irqs)
pirq_table = NULL;
}
}
void __init pcibios_fixup_irqs(void)
{
struct pci_dev *dev;
u8 pin;
DBG("PCI: IRQ fixup\n");
pci_for_each_dev(dev) {
/*
* If the BIOS has set an out of range IRQ number, just ignore it.
* Also keep track of which IRQ's are already in use.
*/
if (dev->irq >= 16) {
DBG("%s: ignoring bogus IRQ %d\n", dev->slot_name, dev->irq);
dev->irq = 0;
}
/* If the IRQ is already assigned to a PCI device, ignore its ISA use penalty */
if (pirq_penalty[dev->irq] >= 100 && pirq_penalty[dev->irq] < 100000)
pirq_penalty[dev->irq] = 0;
pirq_penalty[dev->irq]++;
}
pci_for_each_dev(dev) {
pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin);
#ifdef CONFIG_X86_IO_APIC
/*
* Recalculate IRQ numbers if we use the I/O APIC.
*/
if (io_apic_assign_pci_irqs)
{
int irq;
if (pin) {
pin--; /* interrupt pins are numbered starting from 1 */
irq = IO_APIC_get_PCI_irq_vector(dev->bus->number, PCI_SLOT(dev->devfn), pin);
/*
* Busses behind bridges are typically not listed in the MP-table.
* In this case we have to look up the IRQ based on the parent bus,
* parent slot, and pin number. The SMP code detects such bridged
* busses itself so we should get into this branch reliably.
*/
if (irq < 0 && dev->bus->parent) { /* go back to the bridge */
struct pci_dev * bridge = dev->bus->self;
pin = (pin + PCI_SLOT(dev->devfn)) % 4;
irq = IO_APIC_get_PCI_irq_vector(bridge->bus->number,
PCI_SLOT(bridge->devfn), pin);
if (irq >= 0)
printk(KERN_WARNING "PCI: using PPB(B%d,I%d,P%d) to get irq %d\n",
bridge->bus->number, PCI_SLOT(bridge->devfn), pin, irq);
}
if (irq >= 0) {
printk(KERN_INFO "PCI->APIC IRQ transform: (B%d,I%d,P%d) -> %d\n",
dev->bus->number, PCI_SLOT(dev->devfn), pin, irq);
dev->irq = irq;
}
}
}
#endif
/*
* Still no IRQ? Try to lookup one...
*/
if (pin && !dev->irq)
pcibios_lookup_irq(dev, 0);
}
}
void pcibios_penalize_isa_irq(int irq)
{
/*
* If any ISAPnP device reports an IRQ in its list of possible
* IRQ's, we try to avoid assigning it to PCI devices.
*/
pirq_penalty[irq] += 100;
}
void pcibios_enable_irq(struct pci_dev *dev)
{
u8 pin;
pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin);
if (pin && !pcibios_lookup_irq(dev, 1) && !dev->irq) {
char *msg;
if (io_apic_assign_pci_irqs)
msg = " Probably buggy MP table.";
else if (pci_probe & PCI_BIOS_IRQ_SCAN)
msg = "";
else
msg = " Please try using pci=biosirq.";
printk(KERN_WARNING "PCI: No IRQ known for interrupt pin %c of device %s.%s\n",
'A' + pin - 1, dev->slot_name, msg);
}
}
File diff suppressed because it is too large. Load diff
+402
View File
@@ -0,0 +1,402 @@
/*
* linux/arch/i386/kernel/process.c
*
* Copyright (C) 1995 Linus Torvalds
*
* Pentium III FXSR, SSE support
* Gareth Hughes <gareth@valinux.com>, May 2000
*/
/*
* This file handles the architecture-dependent parts of process handling..
*/
#define __KERNEL_SYSCALLS__
#include <stdarg.h>
#include <xeno/config.h>
#include <xeno/lib.h>
#include <xeno/errno.h>
#include <xeno/sched.h>
#include <xeno/smp.h>
#include <asm/ptrace.h>
#include <xeno/delay.h>
#include <asm/mc146818rtc.h>
#include <asm/system.h>
#include <asm/io.h>
#include <asm/processor.h>
#include <asm/desc.h>
#include <asm/i387.h>
#include <xeno/irq.h>
#include <xeno/event.h>
asmlinkage void ret_from_newdomain(void) __asm__("ret_from_newdomain");
int hlt_counter;
void disable_hlt(void)
{
hlt_counter++;
}
void enable_hlt(void)
{
hlt_counter--;
}
/*
* We use this if we don't have any better
* idle routine..
*/
static void default_idle(void)
{
if (!hlt_counter) {
__cli();
if (!current->hyp_events)
safe_halt();
else
__sti();
}
}
/*
* The idle thread. There's no useful work to be
* done, so just try to conserve power and have a
* low exit latency (ie sit in a loop waiting for
* somebody to say that they'd like to reschedule)
*/
void cpu_idle (void)
{
ASSERT(current->domain == IDLE_DOMAIN_ID);
current->has_cpu = 1;
/*
* Declares CPU setup done to the boot processor.
* Therefore memory barrier to ensure state is visible.
*/
smp_mb();
init_idle();
for ( ; ; )
{
while (!current->hyp_events)
default_idle();
do_hyp_events();
}
}
static long no_idt[2];
static int reboot_mode;
int reboot_thru_bios = 0;
#ifdef CONFIG_SMP
int reboot_smp = 0;
static int reboot_cpu = -1;
/* shamelessly grabbed from lib/vsprintf.c for readability */
#define is_digit(c) ((c) >= '0' && (c) <= '9')
#endif
static inline void kb_wait(void)
{
int i;
for (i=0; i<0x10000; i++)
if ((inb_p(0x64) & 0x02) == 0)
break;
}
void machine_restart(char * __unused)
{
#if CONFIG_SMP
int cpuid;
cpuid = GET_APIC_ID(apic_read(APIC_ID));
if (reboot_smp) {
/* check to see if reboot_cpu is valid
if its not, default to the BSP */
if ((reboot_cpu == -1) ||
(reboot_cpu > (NR_CPUS -1)) ||
!(phys_cpu_present_map & (1<<cpuid)))
reboot_cpu = boot_cpu_physical_apicid;
reboot_smp = 0; /* use this as a flag to only go through this once*/
/* re-run this function on the other CPUs
it will fall though this section since we have
cleared reboot_smp, and do the reboot if it is the
correct CPU, otherwise it halts. */
if (reboot_cpu != cpuid)
smp_call_function((void *)machine_restart , NULL, 1, 0);
}
/* if reboot_cpu is still -1, then we want a tradional reboot,
and if we are not running on the reboot_cpu,, halt */
if ((reboot_cpu != -1) && (cpuid != reboot_cpu)) {
for (;;)
__asm__ __volatile__ ("hlt");
}
/*
* Stop all CPUs and turn off local APICs and the IO-APIC, so
* other OSs see a clean IRQ state.
*/
smp_send_stop();
disable_IO_APIC();
#endif
if(!reboot_thru_bios) {
/* rebooting needs to touch the page at absolute addr 0 */
*((unsigned short *)__va(0x472)) = reboot_mode;
for (;;) {
int i;
for (i=0; i<100; i++) {
kb_wait();
udelay(50);
outb(0xfe,0x64); /* pulse reset low */
udelay(50);
}
/* That didn't work - force a triple fault.. */
__asm__ __volatile__("lidt %0": :"m" (no_idt));
__asm__ __volatile__("int3");
}
}
panic("Need to reinclude BIOS reboot code\n");
}
void machine_halt(void)
{
machine_restart(0);
}
void machine_power_off(void)
{
machine_restart(0);
}
extern void show_trace(unsigned long* esp);
void show_regs(struct pt_regs * regs)
{
unsigned long cr0 = 0L, cr2 = 0L, cr3 = 0L, cr4 = 0L;
printk("\n");
printk("EIP: %04x:[<%08lx>] CPU: %d",0xffff & regs->xcs,regs->eip, smp_processor_id());
if (regs->xcs & 3)
printk(" ESP: %04x:%08lx",0xffff & regs->xss,regs->esp);
printk(" EFLAGS: %08lx\n",regs->eflags);
printk("EAX: %08lx EBX: %08lx ECX: %08lx EDX: %08lx\n",
regs->eax,regs->ebx,regs->ecx,regs->edx);
printk("ESI: %08lx EDI: %08lx EBP: %08lx",
regs->esi, regs->edi, regs->ebp);
printk(" DS: %04x ES: %04x\n",
0xffff & regs->xds,0xffff & regs->xes);
__asm__("movl %%cr0, %0": "=r" (cr0));
__asm__("movl %%cr2, %0": "=r" (cr2));
__asm__("movl %%cr3, %0": "=r" (cr3));
/* This could fault if %cr4 does not exist */
__asm__("1: movl %%cr4, %0 \n"
"2: \n"
".section __ex_table,\"a\" \n"
".long 1b,2b \n"
".previous \n"
: "=r" (cr4): "0" (0));
printk("CR0: %08lx CR2: %08lx CR3: %08lx CR4: %08lx\n", cr0, cr2, cr3, cr4);
show_trace(&regs->esp);
}
/*
* No need to lock the MM as we are the last user
*/
void release_segments(struct mm_struct *mm)
{
#if 0
void * ldt = mm.context.segments;
/*
* free the LDT
*/
if (ldt) {
mm.context.segments = NULL;
clear_LDT();
vfree(ldt);
}
#endif
}
/*
* Free current thread data structures etc..
*/
void exit_thread(void)
{
/* nothing to do ... */
}
void flush_thread(void)
{
struct task_struct *tsk = current;
memset(tsk->thread.debugreg, 0, sizeof(unsigned long)*8);
/*
* Forget coprocessor state..
*/
clear_fpu(tsk);
tsk->flags &= ~PF_DONEFPUINIT;
}
void release_thread(struct task_struct *dead_task)
{
#if 0
if (dead_task->mm) {
void * ldt = dead_task->mm.context.segments;
// temporary debugging check
if (ldt) {
printk("WARNING: dead process %8s still has LDT? <%p>\n",
dead_task->comm, ldt);
BUG();
}
}
#endif
}
/*
* we do not have to muck with descriptors here, that is
* done in switch_mm() as needed.
*/
void copy_segments(struct task_struct *p, struct mm_struct *new_mm)
{
#if 0
struct mm_struct * old_mm;
void *old_ldt, *ldt;
ldt = NULL;
old_mm = current->mm;
if (old_mm && (old_ldt = old_mm.context.segments) != NULL) {
/*
* Completely new LDT, we initialize it from the parent:
*/
ldt = vmalloc(LDT_ENTRIES*LDT_ENTRY_SIZE);
if (!ldt)
printk(KERN_WARNING "ldt allocation failed\n");
else
memcpy(ldt, old_ldt, LDT_ENTRIES*LDT_ENTRY_SIZE);
}
new_mm.context.segments = ldt;
new_mm.context.cpuvalid = ~0UL; /* valid on all CPU's - they can't have stale data */
#endif
}
void new_thread(struct task_struct *p,
unsigned long start_pc,
unsigned long start_stack,
unsigned long start_info)
{
struct pt_regs * regs;
regs = ((struct pt_regs *) (THREAD_SIZE + (unsigned long) p)) - 1;
memset(regs, 0, sizeof(*regs));
/*
* Initial register values:
* DS,ES,FS,GS = __GUEST_DS
* CS:EIP = __GUEST_CS:start_pc
* SS:ESP = __GUEST_DS:start_stack
* ESI = start_info
* [EAX,EBX,ECX,EDX,EDI,EBP are zero]
*/
p->thread.fs = p->thread.gs = __GUEST_DS;
regs->xds = regs->xes = regs->xss = __GUEST_DS;
regs->xcs = __GUEST_CS;
regs->eip = start_pc;
regs->esp = start_stack;
regs->esi = start_info;
p->thread.esp = (unsigned long) regs;
p->thread.esp0 = (unsigned long) (regs+1);
p->thread.eip = (unsigned long) ret_from_newdomain;
__save_flags(regs->eflags);
regs->eflags |= X86_EFLAGS_IF;
}
/*
* This special macro can be used to load a debugging register
*/
#define loaddebug(thread,register) \
__asm__("movl %0,%%db" #register \
: /* no output */ \
:"r" (thread->debugreg[register]))
/*
* switch_to(x,yn) should switch tasks from x to y.
*
* We fsave/fwait so that an exception goes off at the right time
* (as a call from the fsave or fwait in effect) rather than to
* the wrong process. Lazy FP saving no longer makes any sense
* with modern CPU's, and this simplifies a lot of things (SMP
* and UP become the same).
*
* NOTE! We used to use the x86 hardware context switching. The
* reason for not using it any more becomes apparent when you
* try to recover gracefully from saved state that is no longer
* valid (stale segment register values in particular). With the
* hardware task-switch, there is no way to fix up bad state in
* a reasonable manner.
*
* The fact that Intel documents the hardware task-switching to
* be slow is a fairly red herring - this code is not noticeably
* faster. However, there _is_ some room for improvement here,
* so the performance issues may eventually be a valid point.
* More important, however, is the fact that this allows us much
* more flexibility.
*/
/* NB. prev_p passed in %eax, next_p passed in %edx */
void __switch_to(struct task_struct *prev_p, struct task_struct *next_p)
{
struct thread_struct *prev = &prev_p->thread,
*next = &next_p->thread;
struct tss_struct *tss = init_tss + smp_processor_id();
unlazy_fpu(prev_p);
tss->esp0 = next->esp0;
tss->esp1 = next->esp1;
tss->ss1 = next->ss1;
/*
* Save away %fs and %gs. No need to save %es and %ds, as
* those are always kernel segments while inside the kernel.
*/
asm volatile("movl %%fs,%0":"=m" (*(int *)&prev->fs));
asm volatile("movl %%gs,%0":"=m" (*(int *)&prev->gs));
/*
* Restore %fs and %gs.
*/
loadsegment(fs, next->fs);
loadsegment(gs, next->gs);
/*
* Now maybe reload the debug registers
*/
if (next->debugreg[7]){
loaddebug(next, 0);
loaddebug(next, 1);
loaddebug(next, 2);
loaddebug(next, 3);
/* no 4 and 5 */
loaddebug(next, 6);
loaddebug(next, 7);
}
}
+33
View File
@@ -0,0 +1,33 @@
#include <asm/atomic.h>
#include <asm/rwlock.h>
#if defined(CONFIG_SMP)
asm(
"
.align 4
.globl __write_lock_failed
__write_lock_failed:
" LOCK "addl $" RW_LOCK_BIAS_STR ",(%eax)
1: rep; nop
cmpl $" RW_LOCK_BIAS_STR ",(%eax)
jne 1b
" LOCK "subl $" RW_LOCK_BIAS_STR ",(%eax)
jnz __write_lock_failed
ret
.align 4
.globl __read_lock_failed
__read_lock_failed:
lock ; incl (%eax)
1: rep; nop
cmpl $1,(%eax)
js 1b
lock ; decl (%eax)
js __read_lock_failed
ret
"
);
#endif
+333
View File
@@ -0,0 +1,333 @@
#include <xeno/config.h>
#include <xeno/init.h>
#include <xeno/interrupt.h>
#include <xeno/lib.h>
#include <xeno/sched.h>
#include <xeno/bootmem.h>
#include <xeno/pci.h>
#include <asm/bitops.h>
#include <asm/smp.h>
#include <asm/processor.h>
#include <asm/mpspec.h>
#include <asm/apic.h>
#include <asm/desc.h>
struct cpuinfo_x86 boot_cpu_data = { 0 };
/* Lots of nice things, since we only target PPro+. */
unsigned long mmu_cr4_features = X86_CR4_PSE | X86_CR4_PGE;
unsigned long wait_init_idle;
/* Standard macro to see if a specific flag is changeable */
static inline int flag_is_changeable_p(u32 flag)
{
u32 f1, f2;
asm("pushfl\n\t"
"pushfl\n\t"
"popl %0\n\t"
"movl %0,%1\n\t"
"xorl %2,%0\n\t"
"pushl %0\n\t"
"popfl\n\t"
"pushfl\n\t"
"popl %0\n\t"
"popfl\n\t"
: "=&r" (f1), "=&r" (f2)
: "ir" (flag));
return ((f1^f2) & flag) != 0;
}
/* Probe for the CPUID instruction */
static int __init have_cpuid_p(void)
{
return flag_is_changeable_p(X86_EFLAGS_ID);
}
void __init get_cpu_vendor(struct cpuinfo_x86 *c)
{
char *v = c->x86_vendor_id;
if (!strcmp(v, "GenuineIntel"))
c->x86_vendor = X86_VENDOR_INTEL;
else if (!strcmp(v, "AuthenticAMD"))
c->x86_vendor = X86_VENDOR_AMD;
else if (!strcmp(v, "CyrixInstead"))
c->x86_vendor = X86_VENDOR_CYRIX;
else if (!strcmp(v, "UMC UMC UMC "))
c->x86_vendor = X86_VENDOR_UMC;
else if (!strcmp(v, "CentaurHauls"))
c->x86_vendor = X86_VENDOR_CENTAUR;
else if (!strcmp(v, "NexGenDriven"))
c->x86_vendor = X86_VENDOR_NEXGEN;
else if (!strcmp(v, "RiseRiseRise"))
c->x86_vendor = X86_VENDOR_RISE;
else if (!strcmp(v, "GenuineTMx86") ||
!strcmp(v, "TransmetaCPU"))
c->x86_vendor = X86_VENDOR_TRANSMETA;
else
c->x86_vendor = X86_VENDOR_UNKNOWN;
}
static void __init init_intel(struct cpuinfo_x86 *c)
{
/* SEP CPUID bug: Pentium Pro reports SEP but doesn't have it */
if ( c->x86 == 6 && c->x86_model < 3 && c->x86_mask < 3 )
clear_bit(X86_FEATURE_SEP, &c->x86_capability);
}
static void __init init_amd(struct cpuinfo_x86 *c)
{
/* Bit 31 in normal CPUID used for nonstandard 3DNow ID;
3DNow is IDd by bit 31 in extended CPUID (1*32+31) anyway */
clear_bit(0*32+31, &c->x86_capability);
switch(c->x86)
{
case 5:
panic("AMD K6 is not supported.\n");
case 6: /* An Athlon/Duron. We can trust the BIOS probably */
break;
}
}
/*
* This does the hard work of actually picking apart the CPU stuff...
*/
void __init identify_cpu(struct cpuinfo_x86 *c)
{
int junk, i;
u32 xlvl, tfms;
c->x86_vendor = X86_VENDOR_UNKNOWN;
c->cpuid_level = -1; /* CPUID not detected */
c->x86_model = c->x86_mask = 0; /* So far unknown... */
c->x86_vendor_id[0] = '\0'; /* Unset */
memset(&c->x86_capability, 0, sizeof c->x86_capability);
if ( !have_cpuid_p() )
panic("Ancient processors not supported\n");
/* Get vendor name */
cpuid(0x00000000, &c->cpuid_level,
(int *)&c->x86_vendor_id[0],
(int *)&c->x86_vendor_id[8],
(int *)&c->x86_vendor_id[4]);
get_cpu_vendor(c);
if ( c->cpuid_level == 0 )
panic("Decrepit CPUID not supported\n");
cpuid(0x00000001, &tfms, &junk, &junk,
&c->x86_capability[0]);
c->x86 = (tfms >> 8) & 15;
c->x86_model = (tfms >> 4) & 15;
c->x86_mask = tfms & 15;
/* AMD-defined flags: level 0x80000001 */
xlvl = cpuid_eax(0x80000000);
if ( (xlvl & 0xffff0000) == 0x80000000 ) {
if ( xlvl >= 0x80000001 )
c->x86_capability[1] = cpuid_edx(0x80000001);
}
/* Transmeta-defined flags: level 0x80860001 */
xlvl = cpuid_eax(0x80860000);
if ( (xlvl & 0xffff0000) == 0x80860000 ) {
if ( xlvl >= 0x80860001 )
c->x86_capability[2] = cpuid_edx(0x80860001);
}
printk("CPU: Before vendor init, caps: %08x %08x %08x, vendor = %d\n",
c->x86_capability[0],
c->x86_capability[1],
c->x86_capability[2],
c->x86_vendor);
switch ( c->x86_vendor ) {
case X86_VENDOR_INTEL:
init_intel(c);
break;
case X86_VENDOR_AMD:
init_amd(c);
break;
default:
panic("Only support Intel processors (P6+)\n");
}
printk("CPU caps: %08x %08x %08x %08x\n",
c->x86_capability[0],
c->x86_capability[1],
c->x86_capability[2],
c->x86_capability[3]);
/*
* On SMP, boot_cpu_data holds the common feature set between
* all CPUs; so make sure that we indicate which features are
* common between the CPUs. The first time this routine gets
* executed, c == &boot_cpu_data.
*/
if ( c != &boot_cpu_data ) {
/* AND the already accumulated flags with these */
for ( i = 0 ; i < NCAPINTS ; i++ )
boot_cpu_data.x86_capability[i] &= c->x86_capability[i];
}
}
unsigned long cpu_initialized;
void __init cpu_init(void)
{
int nr = smp_processor_id();
struct tss_struct * t = &init_tss[nr];
if ( test_and_set_bit(nr, &cpu_initialized) )
panic("CPU#%d already initialized!!!\n", nr);
printk("Initializing CPU#%d\n", nr);
__asm__ __volatile__("lgdt %0": "=m" (gdt_descr));
__asm__ __volatile__("lidt %0": "=m" (idt_descr));
/* No nested task. */
__asm__("pushfl ; andl $0xffffbfff,(%esp) ; popfl");
/* Set up and load the per-CPU TSS and LDT. */
t->ss0 = __HYPERVISOR_DS;
t->esp0 = current->thread.esp0;
set_tss_desc(nr,t);
load_TR(nr);
__asm__ __volatile__("lldt %%ax"::"a" (0));
/* Clear all 6 debug registers. */
#define CD(register) __asm__("movl %0,%%db" #register ::"r"(0) );
CD(0); CD(1); CD(2); CD(3); /* no db4 and db5 */; CD(6); CD(7);
#undef CD
/* Stick the idle task on the run queue. */
(void)wake_up(current);
}
static void __init do_initcalls(void)
{
initcall_t *call;
call = &__initcall_start;
do {
(*call)();
call++;
} while (call < &__initcall_end);
}
/*
* IBM-compatible BIOSes place drive info tables at initial interrupt
* vectors 0x41 and 0x46. These are in the for of 16-bit-mode far ptrs.
*/
struct drive_info_struct { unsigned char dummy[32]; } drive_info;
void get_bios_driveinfo(void)
{
unsigned long seg, off, tab1, tab2;
off = (unsigned long)*(unsigned short *)(4*0x41+0);
seg = (unsigned long)*(unsigned short *)(4*0x41+2);
tab1 = (seg<<4) + off;
off = (unsigned long)*(unsigned short *)(4*0x46+0);
seg = (unsigned long)*(unsigned short *)(4*0x46+2);
tab2 = (seg<<4) + off;
printk("Reading BIOS drive-info tables at 0x%05lx and 0x%05lx\n",
tab1, tab2);
memcpy(drive_info.dummy+ 0, (char *)tab1, 16);
memcpy(drive_info.dummy+16, (char *)tab2, 16);
}
unsigned long pci_mem_start = 0x10000000;
void __init start_of_day(void)
{
extern void trap_init(void);
extern void init_IRQ(void);
extern void time_init(void);
extern void softirq_init(void);
extern void timer_bh(void);
extern void tqueue_bh(void);
extern void immediate_bh(void);
extern void init_timervecs(void);
extern int setup_network_devices(void);
extern void net_init(void);
unsigned long low_mem_size;
/*
* We do this early, but tables are in the lowest 1MB (usually
* 0xfe000-0xfffff). Therefore they're unlikely to ever get clobbered.
*/
get_bios_driveinfo();
/* Tell the PCI layer not to allocate too close to the RAM area.. */
low_mem_size = ((max_page << PAGE_SHIFT) + 0xfffff) & ~0xfffff;
if ( low_mem_size > pci_mem_start ) pci_mem_start = low_mem_size;
identify_cpu(&boot_cpu_data); /* get CPU type info */
if ( cpu_has_fxsr ) set_in_cr4(X86_CR4_OSFXSR);
if ( cpu_has_xmm ) set_in_cr4(X86_CR4_OSXMMEXCPT);
find_smp_config(); /* find ACPI tables */
smp_alloc_memory(); /* trampoline which other CPUs jump at */
paging_init(); /* not much here now, but sets up fixmap */
if ( smp_found_config ) get_smp_config();
domain_init();
trap_init(); /*
* installs trap (s/w exception) wrappers.
* Most route via entry.S and thence back into traps.c
* where a really simple handler does a panic.
* Instead, we'll want to pass most back to a domain.
*/
init_IRQ(); /* installs simple interrupt wrappers. Starts HZ clock. */
time_init(); /* installs software handler for HZ clock. */
softirq_init();
init_timervecs();
init_bh(TIMER_BH, timer_bh);
init_bh(TQUEUE_BH, tqueue_bh);
init_bh(IMMEDIATE_BH, immediate_bh);
init_apic_mappings(); /* make APICs addressable in our pagetables. */
sti();
#ifndef CONFIG_SMP
APIC_init_uniprocessor();
#else
smp_boot_cpus(); /*
* Does loads of stuff, including kicking the local
* APIC, and the IO APIC after other CPUs are booted.
* Each IRQ is preferably handled by IO-APIC, but
* fall thru to 8259A if we have to (but slower).
*/
#endif
zap_low_mappings();
kmem_cache_init();
kmem_cache_sizes_init(max_page);
#ifdef CONFIG_PCI
pci_init();
#endif
do_initcalls();
if ( !setup_network_devices() )
panic("Must have a network device!\n");
net_init(); /* initializes virtual network system. */
#ifdef CONFIG_SMP
wait_init_idle = cpu_online_map;
clear_bit(smp_processor_id(), &wait_init_idle);
smp_threads_ready = 1;
smp_commence(); /* Tell other CPUs that state of the world is stable. */
while (wait_init_idle)
{
cpu_relax();
barrier();
}
#endif
}
+578
View File
@@ -0,0 +1,578 @@
/*
* Intel SMP support routines.
*
* (c) 1995 Alan Cox, Building #3 <alan@redhat.com>
* (c) 1998-99, 2000 Ingo Molnar <mingo@redhat.com>
*
* This code is released under the GNU General Public License version 2 or
* later.
*/
#include <xeno/irq.h>
#include <xeno/sched.h>
#include <xeno/delay.h>
#include <xeno/spinlock.h>
#include <asm/smp.h>
#include <asm/mc146818rtc.h>
#include <asm/pgalloc.h>
#include <asm/smpboot.h>
/*
* Some notes on x86 processor bugs affecting SMP operation:
*
* Pentium, Pentium Pro, II, III (and all CPUs) have bugs.
* The Linux implications for SMP are handled as follows:
*
* Pentium III / [Xeon]
* None of the E1AP-E3AP errata are visible to the user.
*
* E1AP. see PII A1AP
* E2AP. see PII A2AP
* E3AP. see PII A3AP
*
* Pentium II / [Xeon]
* None of the A1AP-A3AP errata are visible to the user.
*
* A1AP. see PPro 1AP
* A2AP. see PPro 2AP
* A3AP. see PPro 7AP
*
* Pentium Pro
* None of 1AP-9AP errata are visible to the normal user,
* except occasional delivery of 'spurious interrupt' as trap #15.
* This is very rare and a non-problem.
*
* 1AP. Linux maps APIC as non-cacheable
* 2AP. worked around in hardware
* 3AP. fixed in C0 and above steppings microcode update.
* Linux does not use excessive STARTUP_IPIs.
* 4AP. worked around in hardware
* 5AP. symmetric IO mode (normal Linux operation) not affected.
* 'noapic' mode has vector 0xf filled out properly.
* 6AP. 'noapic' mode might be affected - fixed in later steppings
* 7AP. We do not assume writes to the LVT deassering IRQs
* 8AP. We do not enable low power mode (deep sleep) during MP bootup
* 9AP. We do not use mixed mode
*
* Pentium
* There is a marginal case where REP MOVS on 100MHz SMP
* machines with B stepping processors can fail. XXX should provide
* an L1cache=Writethrough or L1cache=off option.
*
* B stepping CPUs may hang. There are hardware work arounds
* for this. We warn about it in case your board doesnt have the work
* arounds. Basically thats so I can tell anyone with a B stepping
* CPU and SMP problems "tough".
*
* Specific items [From Pentium Processor Specification Update]
*
* 1AP. Linux doesn't use remote read
* 2AP. Linux doesn't trust APIC errors
* 3AP. We work around this
* 4AP. Linux never generated 3 interrupts of the same priority
* to cause a lost local interrupt.
* 5AP. Remote read is never used
* 6AP. not affected - worked around in hardware
* 7AP. not affected - worked around in hardware
* 8AP. worked around in hardware - we get explicit CS errors if not
* 9AP. only 'noapic' mode affected. Might generate spurious
* interrupts, we log only the first one and count the
* rest silently.
* 10AP. not affected - worked around in hardware
* 11AP. Linux reads the APIC between writes to avoid this, as per
* the documentation. Make sure you preserve this as it affects
* the C stepping chips too.
* 12AP. not affected - worked around in hardware
* 13AP. not affected - worked around in hardware
* 14AP. we always deassert INIT during bootup
* 15AP. not affected - worked around in hardware
* 16AP. not affected - worked around in hardware
* 17AP. not affected - worked around in hardware
* 18AP. not affected - worked around in hardware
* 19AP. not affected - worked around in BIOS
*
* If this sounds worrying believe me these bugs are either ___RARE___,
* or are signal timing bugs worked around in hardware and there's
* about nothing of note with C stepping upwards.
*/
/* The 'big kernel lock' */
spinlock_t kernel_flag = SPIN_LOCK_UNLOCKED;
struct tlb_state cpu_tlbstate[NR_CPUS] = {[0 ... NR_CPUS-1] = { 0 }};
/*
* the following functions deal with sending IPIs between CPUs.
*
* We use 'broadcast', CPU->CPU IPIs and self-IPIs too.
*/
static inline int __prepare_ICR (unsigned int shortcut, int vector)
{
return APIC_DM_FIXED | shortcut | vector | APIC_DEST_LOGICAL;
}
static inline int __prepare_ICR2 (unsigned int mask)
{
return SET_APIC_DEST_FIELD(mask);
}
static inline void __send_IPI_shortcut(unsigned int shortcut, int vector)
{
/*
* Subtle. In the case of the 'never do double writes' workaround
* we have to lock out interrupts to be safe. As we don't care
* of the value read we use an atomic rmw access to avoid costly
* cli/sti. Otherwise we use an even cheaper single atomic write
* to the APIC.
*/
unsigned int cfg;
/*
* Wait for idle.
*/
apic_wait_icr_idle();
/*
* No need to touch the target chip field
*/
cfg = __prepare_ICR(shortcut, vector);
/*
* Send the IPI. The write to APIC_ICR fires this off.
*/
apic_write_around(APIC_ICR, cfg);
}
void send_IPI_self(int vector)
{
__send_IPI_shortcut(APIC_DEST_SELF, vector);
}
static inline void send_IPI_mask_bitmask(int mask, int vector)
{
unsigned long cfg;
unsigned long flags;
__save_flags(flags);
__cli();
/*
* Wait for idle.
*/
apic_wait_icr_idle();
/*
* prepare target chip field
*/
cfg = __prepare_ICR2(mask);
apic_write_around(APIC_ICR2, cfg);
/*
* program the ICR
*/
cfg = __prepare_ICR(0, vector);
/*
* Send the IPI. The write to APIC_ICR fires this off.
*/
apic_write_around(APIC_ICR, cfg);
__restore_flags(flags);
}
static inline void send_IPI_mask_sequence(int mask, int vector)
{
unsigned long cfg, flags;
unsigned int query_cpu, query_mask;
__save_flags(flags);
__cli();
for (query_cpu = 0; query_cpu < NR_CPUS; ++query_cpu) {
query_mask = 1 << query_cpu;
if (query_mask & mask) {
/*
* Wait for idle.
*/
apic_wait_icr_idle();
/*
* prepare target chip field
*/
cfg = __prepare_ICR2(cpu_to_logical_apicid(query_cpu));
apic_write_around(APIC_ICR2, cfg);
/*
* program the ICR
*/
cfg = __prepare_ICR(0, vector);
/*
* Send the IPI. The write to APIC_ICR fires this off.
*/
apic_write_around(APIC_ICR, cfg);
}
}
__restore_flags(flags);
}
static inline void send_IPI_mask(int mask, int vector)
{
send_IPI_mask_bitmask(mask, vector);
}
static inline void send_IPI_allbutself(int vector)
{
/*
* if there are no other CPUs in the system then
* we get an APIC send error if we try to broadcast.
* thus we have to avoid sending IPIs in this case.
*/
if (!(smp_num_cpus > 1))
return;
__send_IPI_shortcut(APIC_DEST_ALLBUT, vector);
}
static inline void send_IPI_all(int vector)
{
__send_IPI_shortcut(APIC_DEST_ALLINC, vector);
}
/*
* Smarter SMP flushing macros.
* c/o Linus Torvalds.
*
* These mean you can really definitely utterly forget about
* writing to user space from interrupts. (Its not allowed anyway).
*
* Optimizations Manfred Spraul <manfred@colorfullife.com>
*/
static volatile unsigned long flush_cpumask;
static struct mm_struct * flush_mm;
static unsigned long flush_va;
static spinlock_t tlbstate_lock = SPIN_LOCK_UNLOCKED;
#define FLUSH_ALL 0xffffffff
/*
* We cannot call mmdrop() because we are in interrupt context,
* instead update mm.cpu_vm_mask.
*/
static void inline leave_mm (unsigned long cpu)
{
if (cpu_tlbstate[cpu].state == TLBSTATE_OK)
BUG();
clear_bit(cpu, &cpu_tlbstate[cpu].active_mm->cpu_vm_mask);
}
/*
*
* The flush IPI assumes that a thread switch happens in this order:
* [cpu0: the cpu that switches]
* 1) switch_mm() either 1a) or 1b)
* 1a) thread switch to a different mm
* 1a1) clear_bit(cpu, &old_mm.cpu_vm_mask);
* Stop ipi delivery for the old mm. This is not synchronized with
* the other cpus, but smp_invalidate_interrupt ignore flush ipis
* for the wrong mm, and in the worst case we perform a superflous
* tlb flush.
* 1a2) set cpu_tlbstate to TLBSTATE_OK
* Now the smp_invalidate_interrupt won't call leave_mm if cpu0
* was in lazy tlb mode.
* 1a3) update cpu_tlbstate[].active_mm
* Now cpu0 accepts tlb flushes for the new mm.
* 1a4) set_bit(cpu, &new_mm.cpu_vm_mask);
* Now the other cpus will send tlb flush ipis.
* 1a4) change cr3.
* 1b) thread switch without mm change
* cpu_tlbstate[].active_mm is correct, cpu0 already handles
* flush ipis.
* 1b1) set cpu_tlbstate to TLBSTATE_OK
* 1b2) test_and_set the cpu bit in cpu_vm_mask.
* Atomically set the bit [other cpus will start sending flush ipis],
* and test the bit.
* 1b3) if the bit was 0: leave_mm was called, flush the tlb.
* 2) switch %%esp, ie current
*
* The interrupt must handle 2 special cases:
* - cr3 is changed before %%esp, ie. it cannot use current->{active_,}mm.
* - the cpu performs speculative tlb reads, i.e. even if the cpu only
* runs in kernel space, the cpu could load tlb entries for user space
* pages.
*
* The good news is that cpu_tlbstate is local to each cpu, no
* write/read ordering problems.
*/
/*
* TLB flush IPI:
*
* 1) Flush the tlb entries if the cpu uses the mm that's being flushed.
* 2) Leave the mm if we are in the lazy tlb mode.
*/
asmlinkage void smp_invalidate_interrupt (void)
{
unsigned long cpu = smp_processor_id();
if (!test_bit(cpu, &flush_cpumask))
return;
/*
* This was a BUG() but until someone can quote me the
* line from the intel manual that guarantees an IPI to
* multiple CPUs is retried _only_ on the erroring CPUs
* its staying as a return
*
* BUG();
*/
if (flush_mm == cpu_tlbstate[cpu].active_mm) {
if (cpu_tlbstate[cpu].state == TLBSTATE_OK) {
if (flush_va == FLUSH_ALL)
local_flush_tlb();
else
__flush_tlb_one(flush_va);
} else
leave_mm(cpu);
}
ack_APIC_irq();
clear_bit(cpu, &flush_cpumask);
}
static void flush_tlb_others (unsigned long cpumask, struct mm_struct *mm,
unsigned long va)
{
/*
* A couple of (to be removed) sanity checks:
*
* - we do not send IPIs to not-yet booted CPUs.
* - current CPU must not be in mask
* - mask must exist :)
*/
if (!cpumask)
BUG();
if ((cpumask & cpu_online_map) != cpumask)
BUG();
if (cpumask & (1 << smp_processor_id()))
BUG();
if (!mm)
BUG();
/*
* i'm not happy about this global shared spinlock in the
* MM hot path, but we'll see how contended it is.
* Temporarily this turns IRQs off, so that lockups are
* detected by the NMI watchdog.
*/
spin_lock(&tlbstate_lock);
flush_mm = mm;
flush_va = va;
atomic_set_mask(cpumask, &flush_cpumask);
/*
* We have to send the IPI only to
* CPUs affected.
*/
send_IPI_mask(cpumask, INVALIDATE_TLB_VECTOR);
while (flush_cpumask)
/* nothing. lockup detection does not belong here */;
flush_mm = NULL;
flush_va = 0;
spin_unlock(&tlbstate_lock);
}
void flush_tlb_current_task(void)
{
struct mm_struct *mm = &current->mm;
unsigned long cpu_mask = mm->cpu_vm_mask & ~(1 << smp_processor_id());
local_flush_tlb();
if (cpu_mask)
flush_tlb_others(cpu_mask, mm, FLUSH_ALL);
}
void flush_tlb_mm (struct mm_struct * mm)
{
unsigned long cpu_mask = mm->cpu_vm_mask & ~(1 << smp_processor_id());
if (current->active_mm == mm)
local_flush_tlb();
if (cpu_mask)
flush_tlb_others(cpu_mask, mm, FLUSH_ALL);
}
#if 0
void flush_tlb_page(struct vm_area_struct * vma, unsigned long va)
{
struct mm_struct *mm = vma->vm_mm;
unsigned long cpu_mask = mm.cpu_vm_mask & ~(1 << smp_processor_id());
if (current->active_mm == mm) {
if(current->mm)
__flush_tlb_one(va);
else
leave_mm(smp_processor_id());
}
if (cpu_mask)
flush_tlb_others(cpu_mask, mm, va);
}
#endif
static inline void do_flush_tlb_all_local(void)
{
unsigned long cpu = smp_processor_id();
__flush_tlb_all();
if (cpu_tlbstate[cpu].state == TLBSTATE_LAZY)
leave_mm(cpu);
}
static void flush_tlb_all_ipi(void* info)
{
do_flush_tlb_all_local();
}
void flush_tlb_all(void)
{
smp_call_function (flush_tlb_all_ipi,0,1,1);
do_flush_tlb_all_local();
}
void smp_send_event_check_mask(unsigned long cpu_mask)
{
send_IPI_mask(cpu_mask, EVENT_CHECK_VECTOR);
}
/*
* Structure and data for smp_call_function(). This is designed to minimise
* static memory requirements. It also looks cleaner.
*/
static spinlock_t call_lock = SPIN_LOCK_UNLOCKED;
struct call_data_struct {
void (*func) (void *info);
void *info;
atomic_t started;
atomic_t finished;
int wait;
};
static struct call_data_struct * call_data;
/*
* this function sends a 'generic call function' IPI to all other CPUs
* in the system.
*/
int smp_call_function (void (*func) (void *info), void *info, int nonatomic,
int wait)
/*
* [SUMMARY] Run a function on all other CPUs.
* <func> The function to run. This must be fast and non-blocking.
* <info> An arbitrary pointer to pass to the function.
* <nonatomic> currently unused.
* <wait> If true, wait (atomically) until function has completed on other CPUs.
* [RETURNS] 0 on success, else a negative status code. Does not return until
* remote CPUs are nearly ready to execute <<func>> or are or have executed.
*
* You must not call this function with disabled interrupts or from a
* hardware interrupt handler, or bottom halfs.
*/
{
struct call_data_struct data;
int cpus = smp_num_cpus-1;
if (!cpus)
return 0;
data.func = func;
data.info = info;
atomic_set(&data.started, 0);
data.wait = wait;
if (wait)
atomic_set(&data.finished, 0);
spin_lock(&call_lock);
call_data = &data;
wmb();
/* Send a message to all other CPUs and wait for them to respond */
send_IPI_allbutself(CALL_FUNCTION_VECTOR);
/* Wait for response */
while (atomic_read(&data.started) != cpus)
barrier();
if (wait)
while (atomic_read(&data.finished) != cpus)
barrier();
spin_unlock(&call_lock);
return 0;
}
static void stop_this_cpu (void * dummy)
{
/*
* Remove this CPU:
*/
clear_bit(smp_processor_id(), &cpu_online_map);
__cli();
disable_local_APIC();
for(;;) __asm__("hlt");
}
/*
* this function calls the 'stop' function on all other CPUs in the system.
*/
void smp_send_stop(void)
{
smp_call_function(stop_this_cpu, NULL, 1, 0);
smp_num_cpus = 1;
__cli();
disable_local_APIC();
__sti();
}
/*
* Nothing to do, as all the work is done automatically when
* we return from the interrupt.
*/
asmlinkage void smp_event_check_interrupt(void)
{
ack_APIC_irq();
}
asmlinkage void smp_call_function_interrupt(void)
{
void (*func) (void *info) = call_data->func;
void *info = call_data->info;
int wait = call_data->wait;
ack_APIC_irq();
/*
* Notify initiating CPU that I've grabbed the data and am
* about to execute the function
*/
mb();
atomic_inc(&call_data->started);
/*
* At this point the info structure may be out of scope unless wait==1
*/
(*func)(info);
if (wait) {
mb();
atomic_inc(&call_data->finished);
}
}
+942
View File
@@ -0,0 +1,942 @@
/*
* x86 SMP booting functions
*
* (c) 1995 Alan Cox, Building #3 <alan@redhat.com>
* (c) 1998, 1999, 2000 Ingo Molnar <mingo@redhat.com>
*
* Much of the core SMP work is based on previous work by Thomas Radke, to
* whom a great many thanks are extended.
*
* Thanks to Intel for making available several different Pentium,
* Pentium Pro and Pentium-II/Xeon MP machines.
* Original development of Linux SMP code supported by Caldera.
*
* This code is released under the GNU General Public License version 2 or
* later.
*
* Fixes
* Felix Koop : NR_CPUS used properly
* Jose Renau : Handle single CPU case.
* Alan Cox : By repeated request 8) - Total BogoMIP report.
* Greg Wright : Fix for kernel stacks panic.
* Erich Boleyn : MP v1.4 and additional changes.
* Matthias Sattler : Changes for 2.1 kernel map.
* Michel Lespinasse : Changes for 2.1 kernel map.
* Michael Chastain : Change trampoline.S to gnu as.
* Alan Cox : Dumb bug: 'B' step PPro's are fine
* Ingo Molnar : Added APIC timers, based on code
* from Jose Renau
* Ingo Molnar : various cleanups and rewrites
* Tigran Aivazian : fixed "0.00 in /proc/uptime on SMP" bug.
* Maciej W. Rozycki : Bits for genuine 82489DX APICs
* Martin J. Bligh : Added support for multi-quad systems
*/
#include <xeno/config.h>
#include <xeno/init.h>
#include <xeno/interrupt.h>
#include <xeno/irq.h>
#include <asm/pgalloc.h>
#include <asm/mc146818rtc.h>
#include <asm/smpboot.h>
#include <xeno/smp.h>
#include <asm/msr.h>
#include <asm/system.h>
#include <xeno/sched.h>
#include <xeno/delay.h>
#include <xeno/lib.h>
/* Set if we find a B stepping CPU */
static int smp_b_stepping;
/* Setup configured maximum number of CPUs to activate */
static int max_cpus = -1;
/* Total count of live CPUs */
int smp_num_cpus = 1;
/* Bitmask of currently online CPUs */
unsigned long cpu_online_map;
static volatile unsigned long cpu_callin_map;
static volatile unsigned long cpu_callout_map;
/* Per CPU bogomips and other parameters */
struct cpuinfo_x86 cpu_data[NR_CPUS] __cacheline_aligned;
/* Set when the idlers are all forked */
int smp_threads_ready;
/*
* Trampoline 80x86 program as an array.
*/
extern unsigned char trampoline_data [];
extern unsigned char trampoline_end [];
static unsigned char *trampoline_base;
/*
* Currently trivial. Write the real->protected mode
* bootstrap into the page concerned. The caller
* has made sure it's suitably aligned.
*/
static unsigned long __init setup_trampoline(void)
{
memcpy(trampoline_base, trampoline_data, trampoline_end - trampoline_data);
return virt_to_phys(trampoline_base);
}
/*
* We are called very early to get the low memory for the
* SMP bootup trampoline page.
*/
void __init smp_alloc_memory(void)
{
/*
* Has to be in very low memory so we can execute
* real-mode AP code.
*/
trampoline_base = __va(0x90000);
}
/*
* The bootstrap kernel entry code has set these up. Save them for
* a given CPU
*/
void __init smp_store_cpu_info(int id)
{
struct cpuinfo_x86 *c = cpu_data + id;
*c = boot_cpu_data;
c->pte_quick = 0;
c->pmd_quick = 0;
c->pgd_quick = 0;
c->pgtable_cache_sz = 0;
identify_cpu(c);
/*
* Mask B, Pentium, but not Pentium MMX
*/
if (c->x86_vendor == X86_VENDOR_INTEL &&
c->x86 == 5 &&
c->x86_mask >= 1 && c->x86_mask <= 4 &&
c->x86_model <= 3)
/*
* Remember we have B step Pentia with bugs
*/
smp_b_stepping = 1;
}
/*
* Architecture specific routine called by the kernel just before init is
* fired off. This allows the BP to have everything in order [we hope].
* At the end of this all the APs will hit the system scheduling and off
* we go. Each AP will load the system gdt's and jump through the kernel
* init into idle(). At this point the scheduler will one day take over
* and give them jobs to do. smp_callin is a standard routine
* we use to track CPUs as they power up.
*/
static atomic_t smp_commenced = ATOMIC_INIT(0);
void __init smp_commence(void)
{
/*
* Lets the callins below out of their loop.
*/
Dprintk("Setting commenced=1, go go go\n");
wmb();
atomic_set(&smp_commenced,1);
}
/*
* TSC synchronization.
*
* We first check wether all CPUs have their TSC's synchronized,
* then we print a warning if not, and always resync.
*/
static atomic_t tsc_start_flag = ATOMIC_INIT(0);
static atomic_t tsc_count_start = ATOMIC_INIT(0);
static atomic_t tsc_count_stop = ATOMIC_INIT(0);
static unsigned long long tsc_values[NR_CPUS];
#define NR_LOOPS 5
/*
* accurate 64-bit/32-bit division, expanded to 32-bit divisions and 64-bit
* multiplication. Not terribly optimized but we need it at boot time only
* anyway.
*
* result == a / b
* == (a1 + a2*(2^32)) / b
* == a1/b + a2*(2^32/b)
* == a1/b + a2*((2^32-1)/b) + a2/b + (a2*((2^32-1) % b))/b
* ^---- (this multiplication can overflow)
*/
static unsigned long long div64 (unsigned long long a, unsigned long b0)
{
unsigned int a1, a2;
unsigned long long res;
a1 = ((unsigned int*)&a)[0];
a2 = ((unsigned int*)&a)[1];
res = a1/b0 +
(unsigned long long)a2 * (unsigned long long)(0xffffffff/b0) +
a2 / b0 +
(a2 * (0xffffffff % b0)) / b0;
return res;
}
static void __init synchronize_tsc_bp (void)
{
int i;
unsigned long long t0;
unsigned long long sum, avg;
long long delta;
int buggy = 0;
printk("checking TSC synchronization across CPUs: ");
atomic_set(&tsc_start_flag, 1);
wmb();
/*
* We loop a few times to get a primed instruction cache,
* then the last pass is more or less synchronized and
* the BP and APs set their cycle counters to zero all at
* once. This reduces the chance of having random offsets
* between the processors, and guarantees that the maximum
* delay between the cycle counters is never bigger than
* the latency of information-passing (cachelines) between
* two CPUs.
*/
for (i = 0; i < NR_LOOPS; i++) {
/*
* all APs synchronize but they loop on '== num_cpus'
*/
while (atomic_read(&tsc_count_start) != smp_num_cpus-1) mb();
atomic_set(&tsc_count_stop, 0);
wmb();
/*
* this lets the APs save their current TSC:
*/
atomic_inc(&tsc_count_start);
rdtscll(tsc_values[smp_processor_id()]);
/*
* We clear the TSC in the last loop:
*/
if (i == NR_LOOPS-1)
write_tsc(0, 0);
/*
* Wait for all APs to leave the synchronization point:
*/
while (atomic_read(&tsc_count_stop) != smp_num_cpus-1) mb();
atomic_set(&tsc_count_start, 0);
wmb();
atomic_inc(&tsc_count_stop);
}
sum = 0;
for (i = 0; i < smp_num_cpus; i++) {
t0 = tsc_values[i];
sum += t0;
}
avg = div64(sum, smp_num_cpus);
sum = 0;
for (i = 0; i < smp_num_cpus; i++) {
delta = tsc_values[i] - avg;
if (delta < 0)
delta = -delta;
/*
* We report bigger than 2 microseconds clock differences.
*/
if (delta > 2*ticks_per_usec) {
long realdelta;
if (!buggy) {
buggy = 1;
printk("\n");
}
realdelta = div64(delta, ticks_per_usec);
if (tsc_values[i] < avg)
realdelta = -realdelta;
printk("BIOS BUG: CPU#%d improperly initialized, has %ld usecs TSC skew! FIXED.\n",
i, realdelta);
}
sum += delta;
}
if (!buggy)
printk("passed.\n");
}
static void __init synchronize_tsc_ap (void)
{
int i;
/*
* smp_num_cpus is not necessarily known at the time
* this gets called, so we first wait for the BP to
* finish SMP initialization:
*/
while (!atomic_read(&tsc_start_flag)) mb();
for (i = 0; i < NR_LOOPS; i++) {
atomic_inc(&tsc_count_start);
while (atomic_read(&tsc_count_start) != smp_num_cpus) mb();
rdtscll(tsc_values[smp_processor_id()]);
if (i == NR_LOOPS-1)
write_tsc(0, 0);
atomic_inc(&tsc_count_stop);
while (atomic_read(&tsc_count_stop) != smp_num_cpus) mb();
}
}
#undef NR_LOOPS
static atomic_t init_deasserted;
void __init smp_callin(void)
{
int cpuid, phys_id, i;
/*
* If waken up by an INIT in an 82489DX configuration
* we may get here before an INIT-deassert IPI reaches
* our local APIC. We have to wait for the IPI or we'll
* lock up on an APIC access.
*/
while (!atomic_read(&init_deasserted));
/*
* (This works even if the APIC is not enabled.)
*/
phys_id = GET_APIC_ID(apic_read(APIC_ID));
cpuid = smp_processor_id();
if (test_and_set_bit(cpuid, &cpu_online_map)) {
printk("huh, phys CPU#%d, CPU#%d already present??\n",
phys_id, cpuid);
BUG();
}
Dprintk("CPU#%d (phys ID: %d) waiting for CALLOUT\n", cpuid, phys_id);
/*
* STARTUP IPIs are fragile beasts as they might sometimes
* trigger some glue motherboard logic. Complete APIC bus
* silence for 1 second, this overestimates the time the
* boot CPU is spending to send the up to 2 STARTUP IPIs
* by a factor of two. This should be enough.
*/
for ( i = 0; i < 200; i++ )
{
if ( test_bit(cpuid, &cpu_callout_map) ) break;
mdelay(10);
}
if (!test_bit(cpuid, &cpu_callout_map)) {
printk("BUG: CPU%d started up but did not get a callout!\n",
cpuid);
BUG();
}
/*
* the boot CPU has finished the init stage and is spinning
* on callin_map until we finish. We are free to set up this
* CPU, first the APIC. (this is probably redundant on most
* boards)
*/
Dprintk("CALLIN, before setup_local_APIC().\n");
setup_local_APIC();
__sti();
#ifdef CONFIG_MTRR
/*
* Must be done before calibration delay is computed
*/
mtrr_init_secondary_cpu ();
#endif
Dprintk("Stack at about %p\n",&cpuid);
/*
* Save our processor parameters
*/
smp_store_cpu_info(cpuid);
/*
* Allow the master to continue.
*/
set_bit(cpuid, &cpu_callin_map);
/*
* Synchronize the TSC with the BP
*/
synchronize_tsc_ap();
}
int cpucount;
/*
* Activate a secondary processor.
*/
int __init start_secondary(void *unused)
{
extern void cpu_init(void);
/*
* Dont put anything before smp_callin(), SMP
* booting is too fragile that we want to limit the
* things done here to the most necessary things.
*/
cpu_init();
smp_callin();
while (!atomic_read(&smp_commenced))
rep_nop();
/*
* low-memory mappings have been cleared, flush them from the local TLBs
* too.
*/
local_flush_tlb();
cpu_idle();
BUG();
return 0;
}
/*
* Everything has been set up for the secondary
* CPUs - they just need to reload everything
* from the task structure
* This function must not return.
*/
void __init initialize_secondary(void)
{
/*
* We don't actually need to load the full TSS,
* basically just the stack pointer and the eip.
*/
asm volatile(
"movl %0,%%esp\n\t"
"jmp *%1"
:
:"r" (current->thread.esp),"r" (current->thread.eip));
}
extern struct {
void * esp;
unsigned short ss;
} stack_start;
/* which physical APIC ID maps to which logical CPU number */
volatile int physical_apicid_2_cpu[MAX_APICID];
/* which logical CPU number maps to which physical APIC ID */
volatile int cpu_2_physical_apicid[NR_CPUS];
/* which logical APIC ID maps to which logical CPU number */
volatile int logical_apicid_2_cpu[MAX_APICID];
/* which logical CPU number maps to which logical APIC ID */
volatile int cpu_2_logical_apicid[NR_CPUS];
static inline void init_cpu_to_apicid(void)
/* Initialize all maps between cpu number and apicids */
{
int apicid, cpu;
for (apicid = 0; apicid < MAX_APICID; apicid++) {
physical_apicid_2_cpu[apicid] = -1;
logical_apicid_2_cpu[apicid] = -1;
}
for (cpu = 0; cpu < NR_CPUS; cpu++) {
cpu_2_physical_apicid[cpu] = -1;
cpu_2_logical_apicid[cpu] = -1;
}
}
static inline void map_cpu_to_boot_apicid(int cpu, int apicid)
/*
* set up a mapping between cpu and apicid. Uses logical apicids for multiquad,
* else physical apic ids
*/
{
physical_apicid_2_cpu[apicid] = cpu;
cpu_2_physical_apicid[cpu] = apicid;
}
static inline void unmap_cpu_to_boot_apicid(int cpu, int apicid)
/*
* undo a mapping between cpu and apicid. Uses logical apicids for multiquad,
* else physical apic ids
*/
{
physical_apicid_2_cpu[apicid] = -1;
cpu_2_physical_apicid[cpu] = -1;
}
#if APIC_DEBUG
static inline void inquire_remote_apic(int apicid)
{
int i, regs[] = { APIC_ID >> 4, APIC_LVR >> 4, APIC_SPIV >> 4 };
char *names[] = { "ID", "VERSION", "SPIV" };
int timeout, status;
printk("Inquiring remote APIC #%d...\n", apicid);
for (i = 0; i < sizeof(regs) / sizeof(*regs); i++) {
printk("... APIC #%d %s: ", apicid, names[i]);
/*
* Wait for idle.
*/
apic_wait_icr_idle();
apic_write_around(APIC_ICR2, SET_APIC_DEST_FIELD(apicid));
apic_write_around(APIC_ICR, APIC_DM_REMRD | regs[i]);
timeout = 0;
do {
udelay(100);
status = apic_read(APIC_ICR) & APIC_ICR_RR_MASK;
} while (status == APIC_ICR_RR_INPROG && timeout++ < 1000);
switch (status) {
case APIC_ICR_RR_VALID:
status = apic_read(APIC_RRR);
printk("%08x\n", status);
break;
default:
printk("failed\n");
}
}
}
#endif
static int wakeup_secondary_via_INIT(int phys_apicid, unsigned long start_eip)
{
unsigned long send_status = 0, accept_status = 0;
int maxlvt, timeout, num_starts, j;
Dprintk("Asserting INIT.\n");
/*
* Turn INIT on target chip
*/
apic_write_around(APIC_ICR2, SET_APIC_DEST_FIELD(phys_apicid));
/*
* Send IPI
*/
apic_write_around(APIC_ICR, APIC_INT_LEVELTRIG | APIC_INT_ASSERT
| APIC_DM_INIT);
Dprintk("Waiting for send to finish...\n");
timeout = 0;
do {
Dprintk("+");
udelay(100);
send_status = apic_read(APIC_ICR) & APIC_ICR_BUSY;
} while (send_status && (timeout++ < 1000));
mdelay(10);
Dprintk("Deasserting INIT.\n");
/* Target chip */
apic_write_around(APIC_ICR2, SET_APIC_DEST_FIELD(phys_apicid));
/* Send IPI */
apic_write_around(APIC_ICR, APIC_INT_LEVELTRIG | APIC_DM_INIT);
Dprintk("Waiting for send to finish...\n");
timeout = 0;
do {
Dprintk("+");
udelay(100);
send_status = apic_read(APIC_ICR) & APIC_ICR_BUSY;
} while (send_status && (timeout++ < 1000));
atomic_set(&init_deasserted, 1);
/*
* Should we send STARTUP IPIs ?
*
* Determine this based on the APIC version.
* If we don't have an integrated APIC, don't send the STARTUP IPIs.
*/
if (APIC_INTEGRATED(apic_version[phys_apicid]))
num_starts = 2;
else
num_starts = 0;
/*
* Run STARTUP IPI loop.
*/
Dprintk("#startup loops: %d.\n", num_starts);
maxlvt = get_maxlvt();
for (j = 1; j <= num_starts; j++) {
Dprintk("Sending STARTUP #%d.\n",j);
apic_read_around(APIC_SPIV);
apic_write(APIC_ESR, 0);
apic_read(APIC_ESR);
Dprintk("After apic_write.\n");
/*
* STARTUP IPI
*/
/* Target chip */
apic_write_around(APIC_ICR2, SET_APIC_DEST_FIELD(phys_apicid));
/* Boot on the stack */
/* Kick the second */
apic_write_around(APIC_ICR, APIC_DM_STARTUP
| (start_eip >> 12));
/*
* Give the other CPU some time to accept the IPI.
*/
udelay(300);
Dprintk("Startup point 1.\n");
Dprintk("Waiting for send to finish...\n");
timeout = 0;
do {
Dprintk("+");
udelay(100);
send_status = apic_read(APIC_ICR) & APIC_ICR_BUSY;
} while (send_status && (timeout++ < 1000));
/*
* Give the other CPU some time to accept the IPI.
*/
udelay(200);
/*
* Due to the Pentium erratum 3AP.
*/
if (maxlvt > 3) {
apic_read_around(APIC_SPIV);
apic_write(APIC_ESR, 0);
}
accept_status = (apic_read(APIC_ESR) & 0xEF);
if (send_status || accept_status)
break;
}
Dprintk("After Startup.\n");
if (send_status)
printk("APIC never delivered???\n");
if (accept_status)
printk("APIC delivery error (%lx).\n", accept_status);
return (send_status | accept_status);
}
extern unsigned long cpu_initialized;
static void __init do_boot_cpu (int apicid)
/*
* NOTE - on most systems this is a PHYSICAL apic ID, but on multiquad
* (ie clustered apic addressing mode), this is a LOGICAL apic ID.
*/
{
struct task_struct *idle;
unsigned long boot_error = 0;
int timeout, cpu;
unsigned long start_eip;
cpu = ++cpucount;
/*
* We can't use kernel_thread since we must avoid to reschedule the child.
*/
if ( (idle = do_newdomain()) == NULL )
panic("failed 'newdomain' for CPU %d", cpu);
idle->processor = cpu;
idle->domain = IDLE_DOMAIN_ID;
idle->mm.pagetable = mk_pagetable((unsigned long)idle0_pg_table);
map_cpu_to_boot_apicid(cpu, apicid);
idle->thread.esp = idle->thread.esp0 = (unsigned long)idle + THREAD_SIZE;
idle->thread.eip = (unsigned long) start_secondary;
/* start_eip had better be page-aligned! */
start_eip = setup_trampoline();
/* So we see what's up */
printk("Booting processor %d/%d eip %lx\n", cpu, apicid, start_eip);
stack_start.esp = (void *) (1024+PAGE_SIZE+(char *)idle-__PAGE_OFFSET);
/*
* This grunge runs the startup process for
* the targeted processor.
*/
atomic_set(&init_deasserted, 0);
Dprintk("Setting warm reset code and vector.\n");
CMOS_WRITE(0xa, 0xf);
local_flush_tlb();
Dprintk("1.\n");
*((volatile unsigned short *) TRAMPOLINE_HIGH) = start_eip >> 4;
Dprintk("2.\n");
*((volatile unsigned short *) TRAMPOLINE_LOW) = start_eip & 0xf;
Dprintk("3.\n");
/*
* Be paranoid about clearing APIC errors.
*/
if (APIC_INTEGRATED(apic_version[apicid])) {
apic_read_around(APIC_SPIV);
apic_write(APIC_ESR, 0);
apic_read(APIC_ESR);
}
/*
* Status is now clean
*/
boot_error = 0;
/*
* Starting actual IPI sequence...
*/
boot_error = wakeup_secondary_via_INIT(apicid, start_eip);
if (!boot_error) {
/*
* allow APs to start initializing.
*/
Dprintk("Before Callout %d.\n", cpu);
set_bit(cpu, &cpu_callout_map);
Dprintk("After Callout %d.\n", cpu);
/*
* Wait 5s total for a response
*/
for (timeout = 0; timeout < 50000; timeout++) {
if (test_bit(cpu, &cpu_callin_map))
break; /* It has booted */
udelay(100);
}
if (test_bit(cpu, &cpu_callin_map)) {
/* number CPUs logically, starting from 1 (BSP is 0) */
printk("CPU%d has booted.\n", cpu);
} else {
boot_error= 1;
if (*((volatile unsigned char *)phys_to_virt(8192))
== 0xA5)
/* trampoline started but...? */
printk("Stuck ??\n");
else
/* trampoline code not run */
printk("Not responding.\n");
#if APIC_DEBUG
inquire_remote_apic(apicid);
#endif
}
}
if (boot_error) {
/* Try to put things back the way they were before ... */
unmap_cpu_to_boot_apicid(cpu, apicid);
clear_bit(cpu, &cpu_callout_map); /* was set here (do_boot_cpu()) */
clear_bit(cpu, &cpu_initialized); /* was set by cpu_init() */
clear_bit(cpu, &cpu_online_map); /* was set in smp_callin() */
cpucount--;
}
/* mark "stuck" area as not stuck */
*((volatile unsigned long *)phys_to_virt(8192)) = 0;
}
/*
* Cycle through the processors sending APIC IPIs to boot each.
*/
static int boot_cpu_logical_apicid;
/* Where the IO area was mapped on multiquad, always 0 otherwise */
void *xquad_portio = NULL;
void __init smp_boot_cpus(void)
{
int apicid, bit;
#ifdef CONFIG_MTRR
/* Must be done before other processors booted */
mtrr_init_boot_cpu ();
#endif
/* Initialize the logical to physical CPU number mapping */
init_cpu_to_apicid();
/*
* Setup boot CPU information
*/
smp_store_cpu_info(0); /* Final full version of the data */
printk("CPU%d booted\n", 0);
/*
* We have the boot CPU online for sure.
*/
set_bit(0, &cpu_online_map);
boot_cpu_logical_apicid = logical_smp_processor_id();
map_cpu_to_boot_apicid(0, boot_cpu_apicid);
/*
* If we couldnt find an SMP configuration at boot time,
* get out of here now!
*/
if (!smp_found_config) {
printk("SMP motherboard not detected.\n");
io_apic_irqs = 0;
cpu_online_map = phys_cpu_present_map = 1;
smp_num_cpus = 1;
if (APIC_init_uniprocessor())
printk("Local APIC not detected."
" Using dummy APIC emulation.\n");
goto smp_done;
}
/*
* Should not be necessary because the MP table should list the boot
* CPU too, but we do it for the sake of robustness anyway.
*/
if (!test_bit(boot_cpu_physical_apicid, &phys_cpu_present_map)) {
printk("weird, boot CPU (#%d) not listed by the BIOS.\n",
boot_cpu_physical_apicid);
phys_cpu_present_map |= (1 << hard_smp_processor_id());
}
/*
* If we couldn't find a local APIC, then get out of here now!
*/
if (APIC_INTEGRATED(apic_version[boot_cpu_physical_apicid]) &&
!test_bit(X86_FEATURE_APIC, boot_cpu_data.x86_capability)) {
printk("BIOS bug, local APIC #%d not detected!...\n",
boot_cpu_physical_apicid);
printk("... forcing use of dummy APIC emulation. (tell your hw vendor)\n");
io_apic_irqs = 0;
cpu_online_map = phys_cpu_present_map = 1;
smp_num_cpus = 1;
goto smp_done;
}
verify_local_APIC();
/*
* If SMP should be disabled, then really disable it!
*/
if (!max_cpus) {
smp_found_config = 0;
printk("SMP mode deactivated, forcing use of dummy APIC emulation.\n");
io_apic_irqs = 0;
cpu_online_map = phys_cpu_present_map = 1;
smp_num_cpus = 1;
goto smp_done;
}
connect_bsp_APIC();
setup_local_APIC();
if (GET_APIC_ID(apic_read(APIC_ID)) != boot_cpu_physical_apicid)
BUG();
/*
* Scan the CPU present map and fire up the other CPUs via do_boot_cpu
*
* In clustered apic mode, phys_cpu_present_map is a constructed thus:
* bits 0-3 are quad0, 4-7 are quad1, etc. A perverse twist on the
* clustered apic ID.
*/
Dprintk("CPU present map: %lx\n", phys_cpu_present_map);
for (bit = 0; bit < NR_CPUS; bit++) {
apicid = cpu_present_to_apicid(bit);
/*
* Don't even attempt to start the boot CPU!
*/
if (apicid == boot_cpu_apicid)
continue;
if (!(phys_cpu_present_map & (1 << bit)))
continue;
if ((max_cpus >= 0) && (max_cpus <= cpucount+1))
continue;
do_boot_cpu(apicid);
/*
* Make sure we unmap all failed CPUs
*/
if ((boot_apicid_to_cpu(apicid) == -1) &&
(phys_cpu_present_map & (1 << bit)))
printk("CPU #%d not responding - cannot use it.\n",
apicid);
}
/*
* Cleanup possible dangling ends...
*/
/*
* Install writable page 0 entry to set BIOS data area.
*/
local_flush_tlb();
/*
* Paranoid: Set warm reset code and vector here back
* to default values.
*/
CMOS_WRITE(0, 0xf);
*((volatile long *) phys_to_virt(0x467)) = 0;
if (!cpucount) {
printk("Error: only one processor found.\n");
} else {
printk("Total of %d processors activated.\n", cpucount+1);
}
smp_num_cpus = cpucount + 1;
if (smp_b_stepping)
printk("WARNING: SMP operation may"
" be unreliable with B stepping processors.\n");
Dprintk("Boot done.\n");
/*
* Here we can be sure that there is an IO-APIC in the system. Let's
* go and set it up:
*/
if ( nr_ioapics ) setup_IO_APIC();
/* Set up all local APIC timers in the system. */
setup_APIC_clocks();
/* Synchronize the TSC with the AP(s). */
if ( cpucount ) synchronize_tsc_bp();
smp_done:
;
}
+143
View File
@@ -0,0 +1,143 @@
/*
* linux/arch/i386/kernel/time.c
*
* Copyright (C) 1991, 1992, 1995 Linus Torvalds
*/
#include <xeno/errno.h>
#include <xeno/sched.h>
#include <xeno/lib.h>
#include <xeno/config.h>
#include <xeno/init.h>
#include <xeno/interrupt.h>
#include <xeno/time.h>
#include <asm/io.h>
#include <xeno/smp.h>
#include <xeno/irq.h>
#include <asm/msr.h>
#include <asm/mpspec.h>
#include <asm/processor.h>
#include <asm/fixmap.h>
unsigned long cpu_khz; /* Detected as we calibrate the TSC */
unsigned long ticks_per_usec; /* TSC ticks per microsecond. */
spinlock_t rtc_lock = SPIN_LOCK_UNLOCKED;
/*
* timer_interrupt() needs to keep up the real-time clock,
* as well as call the "do_timer()" routine every clocktick
*/
int timer_ack=0;
extern spinlock_t i8259A_lock;
static inline void do_timer_interrupt(int irq,
void *dev_id, struct pt_regs *regs)
{
#ifdef CONFIG_X86_IO_APIC
if (timer_ack) {
/*
* Subtle, when I/O APICs are used we have to ack timer IRQ
* manually to reset the IRR bit for do_slow_gettimeoffset().
* This will also deassert NMI lines for the watchdog if run
* on an 82489DX-based system.
*/
spin_lock(&i8259A_lock);
outb(0x0c, 0x20);
/* Ack the IRQ; AEOI will end it automatically. */
inb(0x20);
spin_unlock(&i8259A_lock);
}
#endif
do_timer(regs);
}
/*
* This is the same as the above, except we _also_ save the current
* Time Stamp Counter value at the time of the timer interrupt, so that
* we later on can estimate the time of day more exactly.
*/
static void timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
do_timer_interrupt(irq, NULL, regs);
}
static struct irqaction irq0 = { timer_interrupt, SA_INTERRUPT, 0, "timer", NULL, NULL};
/* ------ Calibrate the TSC -------
* Return processor ticks per second / CALIBRATE_FRAC.
*/
#define CLOCK_TICK_RATE 1193180 /* system crystal frequency (Hz) */
#define CALIBRATE_FRAC 20 /* calibrate over 50ms */
#define CALIBRATE_LATCH ((CLOCK_TICK_RATE+(CALIBRATE_FRAC/2))/CALIBRATE_FRAC)
static unsigned long __init calibrate_tsc(void)
{
/* Set the Gate high, disable speaker */
outb((inb(0x61) & ~0x02) | 0x01, 0x61);
/*
* Now let's take care of CTC channel 2
*
* Set the Gate high, program CTC channel 2 for mode 0,
* (interrupt on terminal count mode), binary count,
* load 5 * LATCH count, (LSB and MSB) to begin countdown.
*/
outb(0xb0, 0x43); /* binary, mode 0, LSB/MSB, Ch 2 */
outb(CALIBRATE_LATCH & 0xff, 0x42); /* LSB of count */
outb(CALIBRATE_LATCH >> 8, 0x42); /* MSB of count */
{
unsigned long startlow, starthigh;
unsigned long endlow, endhigh;
unsigned long count;
rdtsc(startlow,starthigh);
count = 0;
do {
count++;
} while ((inb(0x61) & 0x20) == 0);
rdtsc(endlow,endhigh);
/* Error: ECTCNEVERSET */
if (count <= 1)
goto bad_ctc;
/* 64-bit subtract - gcc just messes up with long longs */
__asm__("subl %2,%0\n\t"
"sbbl %3,%1"
:"=a" (endlow), "=d" (endhigh)
:"g" (startlow), "g" (starthigh),
"0" (endlow), "1" (endhigh));
/* Error: ECPUTOOFAST */
if (endhigh)
goto bad_ctc;
return endlow;
}
/*
* The CTC wasn't reliable: we got a hit on the very first read, or the
* CPU was so fast/slow that the quotient wouldn't fit in 32 bits..
*/
bad_ctc:
return 0;
}
void __init time_init(void)
{
unsigned long ticks_per_frac = calibrate_tsc();
if ( !ticks_per_frac )
panic("Error calibrating TSC\n");
ticks_per_usec = ticks_per_frac / (1000000/CALIBRATE_FRAC);
cpu_khz = ticks_per_frac / (1000/CALIBRATE_FRAC);
printk("Detected %lu.%03lu MHz processor.\n",
cpu_khz / 1000, cpu_khz % 1000);
setup_irq(0, &irq0);
}
+54
View File
@@ -0,0 +1,54 @@
/*
*
* Trampoline.S Derived from Setup.S by Linus Torvalds
*
* 4 Jan 1997 Michael Chastain: changed to gnu as.
*
* Entry: CS:IP point to the start of our code, we are
* in real mode with no stack, but the rest of the
* trampoline page to make our stack and everything else
* is a mystery.
*
* On entry to trampoline_data, the processor is in real mode
* with 16-bit addressing and 16-bit data. CS has some value
* and IP is zero. Thus, data addresses need to be absolute
* (no relocation) and are taken with regard to r_base.
*/
#include <xeno/config.h>
#include <asm/page.h>
.data
.code16
ENTRY(trampoline_data)
r_base = .
mov %cs, %ax # Code and data in the same place
mov %ax, %ds
movl $0xA5A5A5A5, %ebx # Flag an SMP trampoline
cli # We should be safe anyway
movl $0xA5A5A5A5, trampoline_data - r_base
lidt idt_48 - r_base # load idt with 0, 0
lgdt gdt_48 - r_base # load gdt with whatever is appropriate
xor %ax, %ax
inc %ax # protected mode (PE) bit
lmsw %ax # into protected mode
jmp flush_instr
flush_instr:
ljmpl $__HYPERVISOR_CS, $(MONITOR_BASE)-__PAGE_OFFSET
idt_48:
.word 0 # idt limit = 0
.word 0, 0 # idt base = 0L
gdt_48:
.word 0x0800 # gdt limit = 2048, 256 GDT entries
.long gdt_table-__PAGE_OFFSET # gdt base = gdt (first SMP CPU)
.globl SYMBOL_NAME(trampoline_end)
SYMBOL_NAME_LABEL(trampoline_end)
+595
View File
@@ -0,0 +1,595 @@
/*
* linux/arch/i386/traps.c
*
* Copyright (C) 1991, 1992 Linus Torvalds
*
* Pentium III FXSR, SSE support
* Gareth Hughes <gareth@valinux.com>, May 2000
*/
#include <xeno/config.h>
#include <xeno/init.h>
#include <xeno/interrupt.h>
#include <xeno/sched.h>
#include <xeno/lib.h>
#include <xeno/errno.h>
#include <asm/ptrace.h>
#include <xeno/delay.h>
#include <xeno/spinlock.h>
#include <xeno/irq.h>
#include <asm/system.h>
#include <asm/io.h>
#include <asm/atomic.h>
#include <asm/desc.h>
#include <asm/debugreg.h>
#include <asm/smp.h>
#include <asm/pgalloc.h>
#include <asm/uaccess.h>
#include <asm/i387.h>
#define GTBF_TRAP 1
#define GTBF_TRAP_NOCODE 2
#define GTBF_TRAP_CR2 4
struct guest_trap_bounce {
unsigned long error_code; /* 0 */
unsigned long cr2; /* 4 */
unsigned short flags; /* 8 */
unsigned short cs; /* 10 */
unsigned long eip; /* 12 */
} guest_trap_bounce[NR_CPUS] = { { 0 } };
asmlinkage int hypervisor_call(void);
asmlinkage void lcall7(void);
asmlinkage void lcall27(void);
/*
* The IDT has to be page-aligned to simplify the Pentium
* F0 0F bug workaround.. We have a special link segment
* for this.
*/
struct desc_struct idt_table[256] __attribute__((__section__(".data.idt"))) = { {0, 0}, };
asmlinkage void divide_error(void);
asmlinkage void debug(void);
asmlinkage void nmi(void);
asmlinkage void int3(void);
asmlinkage void overflow(void);
asmlinkage void bounds(void);
asmlinkage void invalid_op(void);
asmlinkage void device_not_available(void);
asmlinkage void double_fault(void);
asmlinkage void coprocessor_segment_overrun(void);
asmlinkage void invalid_TSS(void);
asmlinkage void segment_not_present(void);
asmlinkage void stack_segment(void);
asmlinkage void general_protection(void);
asmlinkage void page_fault(void);
asmlinkage void coprocessor_error(void);
asmlinkage void simd_coprocessor_error(void);
asmlinkage void alignment_check(void);
asmlinkage void spurious_interrupt_bug(void);
asmlinkage void machine_check(void);
int kstack_depth_to_print = 24;
static inline int kernel_text_address(unsigned long addr)
{
return ( 1 );
}
void show_trace(unsigned long * stack)
{
int i;
unsigned long addr;
if (!stack)
stack = (unsigned long*)&stack;
printk("Call Trace: ");
i = 1;
while (((long) stack & (THREAD_SIZE-1)) != 0) {
addr = *stack++;
if (kernel_text_address(addr)) {
if (i && ((i % 6) == 0))
printk("\n ");
printk("[<%08lx>] ", addr);
i++;
}
}
printk("\n");
}
void show_trace_task(struct task_struct *tsk)
{
unsigned long esp = tsk->thread.esp;
/* User space on another CPU? */
if ((esp ^ (unsigned long)tsk) & (PAGE_MASK<<1))
return;
show_trace((unsigned long *)esp);
}
void show_stack(unsigned long * esp)
{
unsigned long *stack;
int i;
// debugging aid: "show_stack(NULL);" prints the
// back trace for this cpu.
if(esp==NULL)
esp=(unsigned long*)&esp;
stack = esp;
for(i=0; i < kstack_depth_to_print; i++) {
if (((long) stack & (THREAD_SIZE-1)) == 0)
break;
if (i && ((i % 8) == 0))
printk("\n ");
printk("%08lx ", *stack++);
}
printk("\n");
show_trace(esp);
}
void show_registers(struct pt_regs *regs)
{
unsigned long esp;
unsigned short ss;
esp = (unsigned long) (&regs->esp);
ss = __HYPERVISOR_DS;
if ( regs->xcs & 3 )
{
esp = regs->esp;
ss = regs->xss & 0xffff;
}
printk("CPU: %d\nEIP: %04x:[<%08lx>] \nEFLAGS: %08lx\n",
smp_processor_id(), 0xffff & regs->xcs, regs->eip, regs->eflags);
printk("eax: %08lx ebx: %08lx ecx: %08lx edx: %08lx\n",
regs->eax, regs->ebx, regs->ecx, regs->edx);
printk("esi: %08lx edi: %08lx ebp: %08lx esp: %08lx\n",
regs->esi, regs->edi, regs->ebp, esp);
printk("ds: %04x es: %04x ss: %04x\n",
regs->xds & 0xffff, regs->xes & 0xffff, ss);
}
spinlock_t die_lock = SPIN_LOCK_UNLOCKED;
void die(const char * str, struct pt_regs * regs, long err)
{
spin_lock_irq(&die_lock);
printk("%s: %04lx,%04lx\n", str, err >> 16, err & 0xffff);
show_registers(regs);
spin_unlock_irq(&die_lock);
panic("HYPERVISOR DEATH!!\n");
}
static inline void die_if_kernel(const char * str, struct pt_regs * regs, long err)
{
if (!(3 & regs->xcs)) die(str, regs, err);
}
static void inline do_trap(int trapnr, char *str,
struct pt_regs * regs,
long error_code, int use_error_code)
{
struct guest_trap_bounce *gtb = guest_trap_bounce+smp_processor_id();
trap_info_t *ti;
unsigned long addr, fixup;
if (!(regs->xcs & 3))
goto fault_in_hypervisor;
ti = current->thread.traps + trapnr;
if ( trapnr == 14 )
{
/* page fault pushes %cr2 */
gtb->flags = GTBF_TRAP_CR2;
__asm__ __volatile__ ("movl %%cr2,%0" : "=r" (gtb->cr2) : );
}
else
{
gtb->flags = use_error_code ? GTBF_TRAP : GTBF_TRAP_NOCODE;
}
gtb->error_code = error_code;
gtb->cs = ti->cs;
gtb->eip = ti->address;
return;
fault_in_hypervisor:
if ( (fixup = search_exception_table(regs->eip)) != 0 )
{
regs->eip = fixup;
return;
}
__asm__ __volatile__ ("movl %%cr2,%0" : "=r" (addr) : );
if ( trapnr == 14 )
{
unsigned long page;
__asm__ __volatile__ ("movl %%cr3,%0" : "=r" (page) : );
printk(" pde = %08lx\n", page);
page = ((unsigned long *) __va(page))[addr >> 22];
printk("*pde = %08lx\n", page);
if ( page & _PAGE_PRESENT )
{
page &= PAGE_MASK;
page = ((unsigned long *) __va(page))[(addr&0x3ff000)>>PAGE_SHIFT];
printk(" *pte = %08lx\n", page);
}
}
show_registers(regs);
panic("CPU%d FATAL TRAP: vector = %d (%s)\n"
"[error_code=%08x]\n"
"Faulting linear address might be %08lx\n",
smp_processor_id(), trapnr, str,
error_code, addr);
}
#define DO_ERROR_NOCODE(trapnr, str, name) \
asmlinkage void do_##name(struct pt_regs * regs, long error_code) \
{ \
do_trap(trapnr, str, regs, error_code, 0); \
}
#define DO_ERROR(trapnr, str, name) \
asmlinkage void do_##name(struct pt_regs * regs, long error_code) \
{ \
do_trap(trapnr, str, regs, error_code, 1); \
}
DO_ERROR_NOCODE( 0, "divide error", divide_error)
DO_ERROR_NOCODE( 3, "int3", int3)
DO_ERROR_NOCODE( 4, "overflow", overflow)
DO_ERROR_NOCODE( 5, "bounds", bounds)
DO_ERROR_NOCODE( 6, "invalid operand", invalid_op)
DO_ERROR_NOCODE( 7, "device not available", device_not_available)
DO_ERROR( 8, "double fault", double_fault)
DO_ERROR_NOCODE( 9, "coprocessor segment overrun", coprocessor_segment_overrun)
DO_ERROR(10, "invalid TSS", invalid_TSS)
DO_ERROR(11, "segment not present", segment_not_present)
DO_ERROR(12, "stack segment", stack_segment)
DO_ERROR(14, "page fault", page_fault)
/* Vector 15 reserved by Intel */
DO_ERROR_NOCODE(16, "fpu error", coprocessor_error)
DO_ERROR(17, "alignment check", alignment_check)
DO_ERROR_NOCODE(18, "machine check", machine_check)
DO_ERROR_NOCODE(19, "simd error", simd_coprocessor_error)
asmlinkage void do_general_protection(struct pt_regs * regs, long error_code)
{
struct guest_trap_bounce *gtb = guest_trap_bounce+smp_processor_id();
trap_info_t *ti;
unsigned long fixup;
/* Bad shit if error in ring 0, or result of an interrupt. */
if (!(regs->xcs & 3) || (error_code & 1))
goto gp_in_kernel;
if ( (error_code & 2) )
{
/* This fault must be due to <INT n> instruction. */
ti = current->thread.traps + (error_code>>3);
if ( ti->dpl >= (regs->xcs & 3) )
{
gtb->flags = GTBF_TRAP_NOCODE;
gtb->cs = ti->cs;
gtb->eip = ti->address;
regs->eip += 2;
return;
}
}
/* Pass on GPF as is. */
ti = current->thread.traps + 13;
gtb->flags = GTBF_TRAP;
gtb->error_code = error_code;
gtb->cs = ti->cs;
gtb->eip = ti->address;
return;
gp_in_kernel:
if ( (fixup = search_exception_table(regs->eip)) != 0 )
{
regs->eip = fixup;
return;
}
die("general protection fault", regs, error_code);
}
static void mem_parity_error(unsigned char reason, struct pt_regs * regs)
{
printk("Uhhuh. NMI received. Dazed and confused, but trying to continue\n");
printk("You probably have a hardware problem with your RAM chips\n");
/* Clear and disable the memory parity error line. */
reason = (reason & 0xf) | 4;
outb(reason, 0x61);
}
static void io_check_error(unsigned char reason, struct pt_regs * regs)
{
unsigned long i;
printk("NMI: IOCK error (debug interrupt?)\n");
show_registers(regs);
/* Re-enable the IOCK line, wait for a few seconds */
reason = (reason & 0xf) | 8;
outb(reason, 0x61);
i = 2000;
while (--i) udelay(1000);
reason &= ~8;
outb(reason, 0x61);
}
static void unknown_nmi_error(unsigned char reason, struct pt_regs * regs)
{
printk("Uhhuh. NMI received for unknown reason %02x.\n", reason);
printk("Dazed and confused, but trying to continue\n");
printk("Do you have a strange power saving mode enabled?\n");
}
asmlinkage void do_nmi(struct pt_regs * regs, long error_code)
{
unsigned char reason = inb(0x61);
if (!(reason & 0xc0)) {
unknown_nmi_error(reason, regs);
return;
}
if (reason & 0x80)
mem_parity_error(reason, regs);
if (reason & 0x40)
io_check_error(reason, regs);
/*
* Reassert NMI in case it became active meanwhile
* as it's edge-triggered.
*/
outb(0x8f, 0x70);
inb(0x71); /* dummy */
outb(0x0f, 0x70);
inb(0x71); /* dummy */
}
asmlinkage void math_state_restore(struct pt_regs *regs, long error_code)
{
__asm__ __volatile__("clts");
if ( !(current->flags & PF_USEDFPU) )
{
if ( current->flags & PF_DONEFPUINIT )
restore_fpu(current);
else
init_fpu();
current->flags |= PF_USEDFPU; /* So we fnsave on switch_to() */
}
if ( current->flags & PF_GUEST_STTS )
{
struct guest_trap_bounce *gtb = guest_trap_bounce+smp_processor_id();
gtb->flags = GTBF_TRAP_NOCODE;
gtb->cs = current->thread.traps[7].cs;
gtb->eip = current->thread.traps[7].address;
current->flags &= ~PF_GUEST_STTS;
}
}
/*
* Our handling of the processor debug registers is non-trivial.
* We do not clear them on entry and exit from the kernel. Therefore
* it is possible to get a watchpoint trap here from inside the kernel.
* However, the code in ./ptrace.c has ensured that the user can
* only set watchpoints on userspace addresses. Therefore the in-kernel
* watchpoint trap can only occur in code which is reading/writing
* from user space. Such code must not hold kernel locks (since it
* can equally take a page fault), therefore it is safe to call
* force_sig_info even though that claims and releases locks.
*
* Code in ./signal.c ensures that the debug control register
* is restored before we deliver any signal, and therefore that
* user code runs with the correct debug control register even though
* we clear it here.
*
* Being careful here means that we don't have to be as careful in a
* lot of more complicated places (task switching can be a bit lazy
* about restoring all the debug state, and ptrace doesn't have to
* find every occurrence of the TF bit that could be saved away even
* by user code)
*/
asmlinkage void do_debug(struct pt_regs * regs, long error_code)
{
unsigned int condition;
struct task_struct *tsk = current;
__asm__ __volatile__("movl %%db6,%0" : "=r" (condition));
/* Mask out spurious debug traps due to lazy DR7 setting */
if (condition & (DR_TRAP0|DR_TRAP1|DR_TRAP2|DR_TRAP3)) {
if (!tsk->thread.debugreg[7])
goto clear_dr7;
}
/* Save debug status register where ptrace can see it */
tsk->thread.debugreg[6] = condition;
panic("trap up to OS here, pehaps\n");
/* Disable additional traps. They'll be re-enabled when
* the signal is delivered.
*/
clear_dr7:
__asm__("movl %0,%%db7"
: /* no output */
: "r" (0));
}
asmlinkage void do_spurious_interrupt_bug(struct pt_regs * regs,
long error_code)
{ /* nothing */ }
#define _set_gate(gate_addr,type,dpl,addr) \
do { \
int __d0, __d1; \
__asm__ __volatile__ ("movw %%dx,%%ax\n\t" \
"movw %4,%%dx\n\t" \
"movl %%eax,%0\n\t" \
"movl %%edx,%1" \
:"=m" (*((long *) (gate_addr))), \
"=m" (*(1+(long *) (gate_addr))), "=&a" (__d0), "=&d" (__d1) \
:"i" ((short) (0x8000+(dpl<<13)+(type<<8))), \
"3" ((char *) (addr)),"2" (__HYPERVISOR_CS << 16)); \
} while (0)
/*
* This needs to use 'idt_table' rather than 'idt', and
* thus use the _nonmapped_ version of the IDT, as the
* Pentium F0 0F bugfix can have resulted in the mapped
* IDT being write-protected.
*/
void set_intr_gate(unsigned int n, void *addr)
{
_set_gate(idt_table+n,14,0,addr);
}
static void __init set_trap_gate(unsigned int n, void *addr)
{
_set_gate(idt_table+n,15,0,addr);
}
static void __init set_system_gate(unsigned int n, void *addr)
{
_set_gate(idt_table+n,15,3,addr);
}
static void __init set_call_gate(void *a, void *addr)
{
_set_gate(a,12,3,addr);
}
#define _set_seg_desc(gate_addr,type,dpl,base,limit) {\
*((gate_addr)+1) = ((base) & 0xff000000) | \
(((base) & 0x00ff0000)>>16) | \
((limit) & 0xf0000) | \
((dpl)<<13) | \
(0x00408000) | \
((type)<<8); \
*(gate_addr) = (((base) & 0x0000ffff)<<16) | \
((limit) & 0x0ffff); }
#define _set_tssldt_desc(n,addr,limit,type) \
__asm__ __volatile__ ("movw %w3,0(%2)\n\t" \
"movw %%ax,2(%2)\n\t" \
"rorl $16,%%eax\n\t" \
"movb %%al,4(%2)\n\t" \
"movb %4,5(%2)\n\t" \
"movb $0,6(%2)\n\t" \
"movb %%ah,7(%2)\n\t" \
"rorl $16,%%eax" \
: "=m"(*(n)) : "a" (addr), "r"(n), "ir"(limit), "i"(type))
void set_tss_desc(unsigned int n, void *addr)
{
_set_tssldt_desc(gdt_table+__TSS(n), (int)addr, 235, 0x89);
}
void set_ldt_desc(unsigned int n, void *addr, unsigned int size)
{
_set_tssldt_desc(gdt_table+__LDT(n), (int)addr, ((size << 3)-1), 0x82);
}
void __init trap_init(void)
{
set_trap_gate(0,&divide_error);
set_trap_gate(1,&debug);
set_intr_gate(2,&nmi);
set_system_gate(3,&int3); /* int3-5 can be called from all */
set_system_gate(4,&overflow);
set_system_gate(5,&bounds);
set_trap_gate(6,&invalid_op);
set_trap_gate(7,&device_not_available);
set_trap_gate(8,&double_fault);
set_trap_gate(9,&coprocessor_segment_overrun);
set_trap_gate(10,&invalid_TSS);
set_trap_gate(11,&segment_not_present);
set_trap_gate(12,&stack_segment);
set_trap_gate(13,&general_protection);
set_intr_gate(14,&page_fault);
set_trap_gate(15,&spurious_interrupt_bug);
set_trap_gate(16,&coprocessor_error);
set_trap_gate(17,&alignment_check);
set_trap_gate(18,&machine_check);
set_trap_gate(19,&simd_coprocessor_error);
/*
* Cunning trick to allow arbitrary "INT n" handling.
*
* 1. 3 <= N <= 5 is trivial, as these are intended to be explicit.
*
* 2. All others, we set gate DPL == 0. Any use of "INT n" will thus
* cause a GPF with CS:EIP pointing at the faulting instruction.
* We can then peek at the instruction at check if it is of the
* form "0xCD <imm8>". If so, we fake out an exception to the
* guest OS. If the protected read page faults, we patch that up as
* a page fault to the guest OS.
* [NB. Of course we check the "soft DPL" to check that guest OS
* wants to handle a particular 'n'. If not, we pass the GPF up
* to the guest OS untouched.]
*
* 3. For efficiency, we may want to allow direct traps by the guest
* OS for certain critical vectors (eg. 0x80 in Linux). These must
* therefore not be mapped by hardware interrupts, and so we'd need
* a static list of them, which we add to on demand.
*/
/* Only ring 1 can access monitor services. */
_set_gate(idt_table+HYPERVISOR_CALL_VECTOR,15,1,&hypervisor_call);
/*
* Should be a barrier for any external CPU state.
*/
{
extern void cpu_init(void);
cpu_init();
}
}
long do_set_trap_table(trap_info_t *traps)
{
trap_info_t cur;
trap_info_t *dst = current->thread.traps;
memset(dst, 0, sizeof(*dst) * 256);
for ( ; ; )
{
if ( copy_from_user(&cur, traps, sizeof(cur)) ) return -EFAULT;
if ( (cur.cs & 3) == 0 ) return -EPERM;
if ( cur.address == 0 ) break;
memcpy(dst+cur.vector, &cur, sizeof(cur));
traps++;
}
return(0);
}
long do_fpu_taskswitch(void)
{
current->flags |= PF_GUEST_STTS;
stts();
return 0;
}
+190
View File
@@ -0,0 +1,190 @@
/*
* User address space access functions.
* The non inlined parts of asm-i386/uaccess.h are here.
*
* Copyright 1997 Andi Kleen <ak@muc.de>
* Copyright 1997 Linus Torvalds
*/
#include <linux/config.h>
#include <asm/uaccess.h>
//#include <asm/mmx.h>
#ifdef CONFIG_X86_USE_3DNOW_AND_WORKS
unsigned long
__generic_copy_to_user(void *to, const void *from, unsigned long n)
{
if (access_ok(VERIFY_WRITE, to, n))
{
if(n<512)
__copy_user(to,from,n);
else
mmx_copy_user(to,from,n);
}
return n;
}
unsigned long
__generic_copy_from_user(void *to, const void *from, unsigned long n)
{
if (access_ok(VERIFY_READ, from, n))
{
if(n<512)
__copy_user_zeroing(to,from,n);
else
mmx_copy_user_zeroing(to, from, n);
}
else
memset(to, 0, n);
return n;
}
#else
unsigned long
__generic_copy_to_user(void *to, const void *from, unsigned long n)
{
prefetch(from);
if (access_ok(VERIFY_WRITE, to, n))
__copy_user(to,from,n);
return n;
}
unsigned long
__generic_copy_from_user(void *to, const void *from, unsigned long n)
{
prefetchw(to);
if (access_ok(VERIFY_READ, from, n))
__copy_user_zeroing(to,from,n);
else
memset(to, 0, n);
return n;
}
#endif
/*
* Copy a null terminated string from userspace.
*/
#define __do_strncpy_from_user(dst,src,count,res) \
do { \
int __d0, __d1, __d2; \
__asm__ __volatile__( \
" testl %1,%1\n" \
" jz 2f\n" \
"0: lodsb\n" \
" stosb\n" \
" testb %%al,%%al\n" \
" jz 1f\n" \
" decl %1\n" \
" jnz 0b\n" \
"1: subl %1,%0\n" \
"2:\n" \
".section .fixup,\"ax\"\n" \
"3: movl %5,%0\n" \
" jmp 2b\n" \
".previous\n" \
".section __ex_table,\"a\"\n" \
" .align 4\n" \
" .long 0b,3b\n" \
".previous" \
: "=d"(res), "=c"(count), "=&a" (__d0), "=&S" (__d1), \
"=&D" (__d2) \
: "i"(-EFAULT), "0"(count), "1"(count), "3"(src), "4"(dst) \
: "memory"); \
} while (0)
long
__strncpy_from_user(char *dst, const char *src, long count)
{
long res;
__do_strncpy_from_user(dst, src, count, res);
return res;
}
long
strncpy_from_user(char *dst, const char *src, long count)
{
long res = -EFAULT;
if (access_ok(VERIFY_READ, src, 1))
__do_strncpy_from_user(dst, src, count, res);
return res;
}
/*
* Zero Userspace
*/
#define __do_clear_user(addr,size) \
do { \
int __d0; \
__asm__ __volatile__( \
"0: rep; stosl\n" \
" movl %2,%0\n" \
"1: rep; stosb\n" \
"2:\n" \
".section .fixup,\"ax\"\n" \
"3: lea 0(%2,%0,4),%0\n" \
" jmp 2b\n" \
".previous\n" \
".section __ex_table,\"a\"\n" \
" .align 4\n" \
" .long 0b,3b\n" \
" .long 1b,2b\n" \
".previous" \
: "=&c"(size), "=&D" (__d0) \
: "r"(size & 3), "0"(size / 4), "1"(addr), "a"(0)); \
} while (0)
unsigned long
clear_user(void *to, unsigned long n)
{
if (access_ok(VERIFY_WRITE, to, n))
__do_clear_user(to, n);
return n;
}
unsigned long
__clear_user(void *to, unsigned long n)
{
__do_clear_user(to, n);
return n;
}
/*
* Return the size of a string (including the ending 0)
*
* Return 0 on exception, a value greater than N if too long
*/
long strnlen_user(const char *s, long n)
{
unsigned long mask = -__addr_ok(s);
unsigned long res, tmp;
__asm__ __volatile__(
" testl %0, %0\n"
" jz 3f\n"
" andl %0,%%ecx\n"
"0: repne; scasb\n"
" setne %%al\n"
" subl %%ecx,%0\n"
" addl %0,%%eax\n"
"1:\n"
".section .fixup,\"ax\"\n"
"2: xorl %%eax,%%eax\n"
" jmp 1b\n"
"3: movb $1,%%al\n"
" jmp 1b\n"
".previous\n"
".section __ex_table,\"a\"\n"
" .align 4\n"
" .long 0b,2b\n"
".previous"
:"=r" (n), "=D" (s), "=a" (res), "=c" (tmp)
:"0" (n), "1" (s), "2" (0), "3" (mask)
:"cc");
return res & mask;
}
+87
View File
@@ -0,0 +1,87 @@
/* ld script to make i386 Linux kernel
* Written by Martin Mares <mj@atrey.karlin.mff.cuni.cz>;
*/
OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386")
OUTPUT_ARCH(i386)
ENTRY(start)
SECTIONS
{
. = 0xE0000000 + 0x100000;
_text = .; /* Text and read-only data */
.text : {
*(.text)
*(.fixup)
*(.gnu.warning)
} = 0x9090
.text.lock : { *(.text.lock) } /* out-of-line lock text */
_etext = .; /* End of text section */
.rodata : { *(.rodata) *(.rodata.*) }
.kstrtab : { *(.kstrtab) }
. = ALIGN(16); /* Exception table */
__start___ex_table = .;
__ex_table : { *(__ex_table) }
__stop___ex_table = .;
__start___ksymtab = .; /* Kernel symbol table */
__ksymtab : { *(__ksymtab) }
__stop___ksymtab = .;
__start___kallsyms = .; /* All kernel symbols */
__kallsyms : { *(__kallsyms) }
__stop___kallsyms = .;
.data : { /* Data */
*(.data)
CONSTRUCTORS
}
_edata = .; /* End of data section */
. = ALIGN(8192); /* init_task */
.data.init_task : { *(.data.init_task) }
. = ALIGN(4096); /* Init code and data */
__init_begin = .;
.text.init : { *(.text.init) }
.data.init : { *(.data.init) }
. = ALIGN(16);
__setup_start = .;
.setup.init : { *(.setup.init) }
__setup_end = .;
__initcall_start = .;
.initcall.init : { *(.initcall.init) }
__initcall_end = .;
. = ALIGN(4096);
__init_end = .;
. = ALIGN(4096);
.data.page_aligned : { *(.data.idt) }
. = ALIGN(32);
.data.cacheline_aligned : { *(.data.cacheline_aligned) }
__bss_start = .; /* BSS */
.bss : {
*(.bss)
}
_end = . ;
/* Sections to be discarded */
/DISCARD/ : {
*(.text.exit)
*(.data.exit)
*(.exitcall.exit)
}
/* Stabs debugging sections. */
.stab 0 : { *(.stab) }
.stabstr 0 : { *(.stabstr) }
.stab.excl 0 : { *(.stab.excl) }
.stab.exclstr 0 : { *(.stab.exclstr) }
.stab.index 0 : { *(.stab.index) }
.stab.indexstr 0 : { *(.stab.indexstr) }
.comment 0 : { *(.comment) }
}
+8
View File
@@ -0,0 +1,8 @@
include $(BASEDIR)/Rules.mk
default: $(OBJS)
$(LD) -r -o common.o $(OBJS)
clean:
rm -f *.o *~ core
+21
View File
@@ -0,0 +1,21 @@
/* block.c
*
* ring data structures for buffering messages between hypervisor and
* guestos's.
*
*/
#include <hypervisor-ifs/block.h>
#include <xeno/lib.h>
/*
* create_block_ring
*
* domain:
*
* allocates space for a particular domain's block io ring.
*/
blk_ring_t *create_block_ring(int domain)
{
printk ("XEN create block ring");
}
+69
View File
@@ -0,0 +1,69 @@
/*
*
* linux/lib/brlock.c
*
* 'Big Reader' read-write spinlocks. See linux/brlock.h for details.
*
* Copyright 2000, Ingo Molnar <mingo@redhat.com>
* Copyright 2000, David S. Miller <davem@redhat.com>
*/
#include <linux/config.h>
#ifdef CONFIG_SMP
#include <linux/sched.h>
#include <linux/brlock.h>
#ifdef __BRLOCK_USE_ATOMICS
brlock_read_lock_t __brlock_array[NR_CPUS][__BR_IDX_MAX] =
{ [0 ... NR_CPUS-1] = { [0 ... __BR_IDX_MAX-1] = RW_LOCK_UNLOCKED } };
void __br_write_lock (enum brlock_indices idx)
{
int i;
for (i = 0; i < smp_num_cpus; i++)
write_lock(&__brlock_array[cpu_logical_map(i)][idx]);
}
void __br_write_unlock (enum brlock_indices idx)
{
int i;
for (i = 0; i < smp_num_cpus; i++)
write_unlock(&__brlock_array[cpu_logical_map(i)][idx]);
}
#else /* ! __BRLOCK_USE_ATOMICS */
brlock_read_lock_t __brlock_array[NR_CPUS][__BR_IDX_MAX] =
{ [0 ... NR_CPUS-1] = { [0 ... __BR_IDX_MAX-1] = 0 } };
struct br_wrlock __br_write_locks[__BR_IDX_MAX] =
{ [0 ... __BR_IDX_MAX-1] = { SPIN_LOCK_UNLOCKED } };
void __br_write_lock (enum brlock_indices idx)
{
int i;
again:
spin_lock(&__br_write_locks[idx].lock);
for (i = 0; i < smp_num_cpus; i++)
if (__brlock_array[cpu_logical_map(i)][idx] != 0) {
spin_unlock(&__br_write_locks[idx].lock);
barrier();
cpu_relax();
goto again;
}
}
void __br_write_unlock (enum brlock_indices idx)
{
spin_unlock(&__br_write_locks[idx].lock);
}
#endif /* __BRLOCK_USE_ATOMICS */
#endif /* CONFIG_SMP */
+90
View File
@@ -0,0 +1,90 @@
/******************************************************************************
* dom0_ops.c
*
* Process command requests from domain-0 guest OS.
*
* Copyright (c) 2002, K A Fraser
*/
#include <xeno/config.h>
#include <xeno/types.h>
#include <xeno/lib.h>
#include <xeno/dom0_ops.h>
#include <xeno/sched.h>
#include <xeno/event.h>
static unsigned int get_domnr(void)
{
struct task_struct *p = &idle0_task;
unsigned long dom_mask = 0;
read_lock_irq(&tasklist_lock);
do {
if ( is_idle_task(p) ) continue;
set_bit(p->domain, &dom_mask);
}
while ( (p = p->next_task) != &idle0_task );
read_unlock_irq(&tasklist_lock);
return (dom_mask == ~0UL) ? 0 : ffz(dom_mask);
}
long do_dom0_op(dom0_op_t *u_dom0_op)
{
long ret = 0;
dom0_op_t op;
if ( current->domain != 0 )
return -EPERM;
if ( copy_from_user(&op, u_dom0_op, sizeof(op)) )
return -EFAULT;
switch ( op.cmd )
{
case DOM0_NEWDOMAIN:
{
struct task_struct *p;
static unsigned int pro = 0;
unsigned int dom = get_domnr();
ret = -ENOMEM;
if ( !dom ) break;
p = do_newdomain();
if ( !p ) break;
p->domain = dom;
pro = (pro+1) % smp_num_cpus;
p->processor = pro;
ret = setup_guestos(p, &op.u.newdomain); /* Load guest OS into @p */
if ( ret != 0 )
{
p->state = TASK_DYING;
release_task(p);
break;
}
wake_up(p); /* Put @p on runqueue */
reschedule(p); /* Force a scheduling decision on @p's CPU */
ret = p->domain;
}
break;
case DOM0_KILLDOMAIN:
{
unsigned int dom = op.u.killdomain.domain;
if ( dom == IDLE_DOMAIN_ID )
{
ret = -EPERM;
}
else
{
ret = kill_other_domain(dom);
}
}
break;
default:
ret = -ENOSYS;
}
return ret;
}
+654
View File
@@ -0,0 +1,654 @@
#include <xeno/config.h>
#include <xeno/init.h>
#include <xeno/lib.h>
#include <xeno/errno.h>
#include <xeno/sched.h>
#include <xeno/mm.h>
#include <xeno/skbuff.h>
#include <xeno/interrupt.h>
#include <xeno/delay.h>
#include <xeno/event.h>
#include <xeno/dom0_ops.h>
#include <asm/io.h>
rwlock_t tasklist_lock __cacheline_aligned = RW_LOCK_UNLOCKED;
schedule_data_t schedule_data[NR_CPUS];
int wake_up(struct task_struct *p)
{
unsigned long flags;
int ret = 0;
spin_lock_irqsave(&schedule_data[p->processor].lock, flags);
if ( __task_on_runqueue(p) ) goto out;
p->state = TASK_RUNNING;
__add_to_runqueue(p);
ret = 1;
out:
spin_unlock_irqrestore(&schedule_data[p->processor].lock, flags);
return ret;
}
struct task_struct *do_newdomain(void)
{
int retval;
struct task_struct *p = NULL;
retval = -ENOMEM;
p = alloc_task_struct();
if (!p) goto newdomain_out;
memset(p, 0, sizeof(*p));
p->shared_info = (void *)get_free_page(GFP_KERNEL);
memset(p->shared_info, 0, sizeof(shared_info_t));
p->addr_limit = USER_DS;
p->state = TASK_UNINTERRUPTIBLE;
p->active_mm = &p->mm;
p->num_net_vifs = 0;
/*
* KAF: Passing in newdomain struct to this function is gross!
* Therefore, for now we just allocate the single blk_ring
* before the multiople net_rings :-)
*/
p->blk_ring_base = (blk_ring_t *)(p->shared_info + 1);
p->net_ring_base = (net_ring_t *)(p->blk_ring_base + 1);
p->pg_head = p->pg_tail = p->tot_pages = 0;
write_lock_irq(&tasklist_lock);
SET_LINKS(p);
write_unlock_irq(&tasklist_lock);
newdomain_out:
return(p);
}
void reschedule(struct task_struct *p)
{
int cpu = p->processor;
struct task_struct *curr;
unsigned long flags;
if ( p->has_cpu ) return;
spin_lock_irqsave(&schedule_data[cpu].lock, flags);
curr = schedule_data[cpu].curr;
if ( is_idle_task(curr) )
{
set_bit(_HYP_EVENT_NEED_RESCHED, &curr->hyp_events);
spin_unlock_irqrestore(&schedule_data[cpu].lock, flags);
#ifdef CONFIG_SMP
if ( cpu != smp_processor_id() ) smp_send_event_check_cpu(cpu);
#endif
}
else
{
spin_unlock_irqrestore(&schedule_data[cpu].lock, flags);
}
}
static void process_timeout(unsigned long __data)
{
struct task_struct * p = (struct task_struct *) __data;
wake_up(p);
}
long schedule_timeout(long timeout)
{
struct timer_list timer;
unsigned long expire;
switch (timeout)
{
case MAX_SCHEDULE_TIMEOUT:
/*
* These two special cases are useful to be comfortable in the caller.
* Nothing more. We could take MAX_SCHEDULE_TIMEOUT from one of the
* negative value but I' d like to return a valid offset (>=0) to allow
* the caller to do everything it want with the retval.
*/
schedule();
goto out;
default:
/*
* Another bit of PARANOID. Note that the retval will be 0 since no
* piece of kernel is supposed to do a check for a negative retval of
* schedule_timeout() (since it should never happens anyway). You just
* have the printk() that will tell you if something is gone wrong and
* where.
*/
if (timeout < 0)
{
printk(KERN_ERR "schedule_timeout: wrong timeout "
"value %lx from %p\n", timeout,
__builtin_return_address(0));
current->state = TASK_RUNNING;
goto out;
}
}
expire = timeout + jiffies;
init_timer(&timer);
timer.expires = expire;
timer.data = (unsigned long) current;
timer.function = process_timeout;
add_timer(&timer);
schedule();
del_timer_sync(&timer);
timeout = expire - jiffies;
out:
return timeout < 0 ? 0 : timeout;
}
long do_yield(void)
{
current->state = TASK_INTERRUPTIBLE;
schedule();
return 0;
}
/* Get a pointer to the specified domain. Consider replacing this
* with a hash lookup later.
*
* Also, kill_other_domain should call this instead of scanning on its own.
*/
struct task_struct *find_domain_by_id(unsigned int dom)
{
struct task_struct *p = &idle0_task;
read_lock_irq(&tasklist_lock);
do {
if ( (p->domain == dom) ) {
read_unlock_irq(&tasklist_lock);
return (p);
}
} while ( (p = p->next_task) != &idle0_task );
read_unlock_irq(&tasklist_lock);
return 0;
}
void kill_domain_with_errmsg(const char *err)
{
printk("DOM%d FATAL ERROR: %s\n",
current->domain, err);
kill_domain();
}
/* Kill the currently executing domain. */
void kill_domain(void)
{
if ( current->domain == 0 )
{
extern void machine_restart(char *);
printk("Domain 0 killed: rebooting machine!\n");
machine_restart(0);
}
printk("Killing domain %d\n", current->domain);
current->state = TASK_DYING;
schedule();
BUG(); /* never get here */
}
long kill_other_domain(unsigned int dom)
{
struct task_struct *p = &idle0_task;
unsigned long cpu_mask = 0;
long ret = -ESRCH;
read_lock_irq(&tasklist_lock);
do {
if ( p->domain == dom )
{
cpu_mask = mark_guest_event(p, _EVENT_DIE);
ret = 0;
break;
}
}
while ( (p = p->next_task) != &idle0_task );
read_unlock_irq(&tasklist_lock);
hyp_event_notify(cpu_mask);
return ret;
}
/* Release resources belonging to task @p. */
void release_task(struct task_struct *p)
{
ASSERT(!__task_on_runqueue(p));
ASSERT(p->state == TASK_DYING);
ASSERT(!p->has_cpu);
write_lock_irq(&tasklist_lock);
REMOVE_LINKS(p);
write_unlock_irq(&tasklist_lock);
/*
* Safe! Only queue skbuffs with tasklist_lock held.
* Only access shared_info with tasklist_lock held.
* And free_task_struct() only releases if refcnt == 0.
*/
while ( p->num_net_vifs )
{
destroy_net_vif(p);
}
free_page((unsigned long)p->shared_info);
free_task_struct(p);
}
asmlinkage void schedule(void)
{
struct task_struct *prev, *next, *p;
struct list_head *tmp;
int this_cpu;
need_resched_back:
prev = current;
this_cpu = prev->processor;
spin_lock_irq(&schedule_data[this_cpu].lock);
ASSERT(!in_interrupt());
ASSERT(__task_on_runqueue(prev));
if ( !prev->counter )
{
prev->counter = 2;
__move_last_runqueue(prev);
}
switch ( prev->state )
{
case TASK_INTERRUPTIBLE:
if ( signal_pending(prev) )
{
prev->state = TASK_RUNNING;
break;
}
default:
__del_from_runqueue(prev);
case TASK_RUNNING:;
}
clear_bit(_HYP_EVENT_NEED_RESCHED, &prev->hyp_events);
next = NULL;
list_for_each(tmp, &schedule_data[smp_processor_id()].runqueue) {
p = list_entry(tmp, struct task_struct, run_list);
next = p;
break;
}
prev->has_cpu = 0;
next->has_cpu = 1;
schedule_data[this_cpu].prev = prev;
schedule_data[this_cpu].curr = next;
spin_unlock_irq(&schedule_data[this_cpu].lock);
if ( unlikely(prev == next) )
{
/* We won't go through the normal tail, so do this by hand */
prev->policy &= ~SCHED_YIELD;
goto same_process;
}
prepare_to_switch();
switch_to(prev, next);
prev = schedule_data[this_cpu].prev;
prev->policy &= ~SCHED_YIELD;
if ( prev->state == TASK_DYING ) release_task(prev);
same_process:
if ( test_bit(_HYP_EVENT_NEED_RESCHED, &current->hyp_events) )
goto need_resched_back;
return;
}
static unsigned int alloc_new_dom_mem(struct task_struct *p, unsigned int kbytes)
{
struct list_head *temp;
struct pfn_info *pf;
unsigned int alloc_pfns;
unsigned int req_pages;
/* how many pages do we need to alloc? */
req_pages = kbytes >> (PAGE_SHIFT - 10);
/* is there enough mem to serve the request? */
if(req_pages > free_pfns)
return -1;
/* allocate pages and build a thread through frame_table */
temp = free_list.next;
printk("bd240 debug: DOM%d requesting %d pages\n", p->domain, req_pages);
for(alloc_pfns = req_pages; alloc_pfns; alloc_pfns--){
pf = list_entry(temp, struct pfn_info, list);
pf->flags |= p->domain;
temp = temp->next;
list_del(&pf->list);
if(p->pg_tail){
pf->next = p->pg_tail;
(frame_table + pf->next)->prev = p->pg_tail = (pf - frame_table);
} else {
p->pg_head = (pf - frame_table);
p->pg_tail = p->pg_head;
pf->next = 0;
pf->prev = 0;
}
pf->prev = 0;
free_pfns--;
}
p->tot_pages = req_pages;
return 0;
}
/*
* Initial load map:
* start_address:
* OS image
* ....
* stack_start:
* start_info:
* <one page>
* page tables:
* <enough pages>
* end_address:
* shared_info:
* <one page>
*/
#define MB_PER_DOMAIN 16
#include <asm/msr.h>
#include <xeno/multiboot.h>
extern int nr_mods;
extern module_t *mod;
extern unsigned char *cmdline;
int setup_guestos(struct task_struct *p, dom0_newdomain_t *params)
{
#define L2_PROT (_PAGE_PRESENT|_PAGE_RW|_PAGE_USER|_PAGE_ACCESSED)
#define L1_PROT (_PAGE_PRESENT|_PAGE_RW|_PAGE_USER|_PAGE_ACCESSED|_PAGE_DIRTY)
#define ALLOC_PAGE_FROM_DOMAIN() \
({ alloc_address -= PAGE_SIZE; __va(alloc_address); })
char *src, *dst;
int i, dom = p->domain;
unsigned long start_address = MAX_MONITOR_ADDRESS;
unsigned long cur_address, end_address, alloc_address, vaddr;
unsigned long virt_load_address, virt_stack_address, virt_shinfo_address;
unsigned long virt_ftable_start_addr = 0, virt_ftable_end_addr;
unsigned int ft_size = 0;
start_info_t *virt_startinfo_address;
unsigned long long time;
l2_pgentry_t *l2tab;
l1_pgentry_t *l1tab = NULL;
struct pfn_info *page = NULL;
net_ring_t *net_ring;
blk_ring_t *blk_ring;
if ( strncmp(__va(mod[0].mod_start), "XenoGues", 8) )
{
printk("DOM%d: Invalid guest OS image\n", dom);
return -1;
}
virt_load_address = *(unsigned long *)__va(mod[0].mod_start + 8);
if ( (virt_load_address & (PAGE_SIZE-1)) )
{
printk("DOM%d: Guest OS load address not page-aligned (%08lx)\n",
dom, virt_load_address);
return -1;
}
if ( alloc_new_dom_mem(p, params->memory_kb) ) return -ENOMEM;
/* temporary, *_address have to be reimplemented in another way
* as we can no longer expect contiguous addr space
*/
start_address = p->pg_head << PAGE_SHIFT;
alloc_address = end_address = start_address + (p->tot_pages << PAGE_SHIFT);
/* start_address += (dom * MB_PER_DOMAIN) << 20; */ /* MB -> bytes */
/* alloc_address = end_address = start_address + (MB_PER_DOMAIN << 20); */
if ( (mod[nr_mods-1].mod_end-mod[0].mod_start) >
((end_address-start_address)>>1) )
{
printk("DOM%d: Guest OS image is too large\n"
" (%luMB is greater than %luMB limit for a\n"
" %luMB address space)\n",
dom, (mod[nr_mods-1].mod_end-mod[0].mod_start)>>20,
(end_address-start_address)>>21,
(end_address-start_address)>>20);
/* XXX Should release memory from alloc_new_dom_mem here XXX */
return -1;
}
/* Set up initial mappings. */
printk("DOM%d: Mapping physmem %08lx -> %08lx (%luMB)\n", dom,
start_address, end_address, (end_address-start_address)>>20);
printk("DOM%d: Guest OS virtual load address is %08lx\n", dom,
virt_load_address);
l2tab = (l2_pgentry_t *)ALLOC_PAGE_FROM_DOMAIN();
memcpy(l2tab, idle0_pg_table, sizeof(idle0_pg_table));
memset(l2tab, 0, DOMAIN_ENTRIES_PER_L2_PAGETABLE*sizeof(l2_pgentry_t));
p->mm.pagetable = mk_pagetable((unsigned long)l2tab);
/*
* NB. The upper limit on this loop does one extra page. This is to
* make sure a pte exists when we want to map the shared_info struct.
*/
/* bd240: not only one extra page but one + num of pages required for
* frame_table if domain 0 is in question. this ugly for loop
* condition is going to change once domain building is moved out
* of hypervisor.
*/
if(dom == 0)
ft_size = frame_table_size;
l2tab = pagetable_ptr(p->mm.pagetable) +
l2_table_offset(virt_load_address);
for ( cur_address = start_address;
cur_address != (end_address + PAGE_SIZE + ft_size);
cur_address += PAGE_SIZE )
{
if ( !((unsigned long)l1tab & (PAGE_SIZE-1)) )
{
l1tab = (l1_pgentry_t *)ALLOC_PAGE_FROM_DOMAIN();
clear_page(l1tab);
l1tab += l1_table_offset(
virt_load_address + cur_address - start_address);
*l2tab++ = mk_l2_pgentry(__pa(l1tab)|L2_PROT);
}
*l1tab++ = mk_l1_pgentry(cur_address|L1_PROT);
/* New domain doesn't own shared_info page, or frame_table. */
if ( cur_address < end_address )
{
page = frame_table + (cur_address >> PAGE_SHIFT);
page->flags = dom | PGT_writeable_page;
page->type_count = page->tot_count = 1;
}
}
/* Pages that are part of page tables must be read-only. */
vaddr = virt_load_address + alloc_address - start_address;
l2tab = pagetable_ptr(p->mm.pagetable) + l2_table_offset(vaddr);
l1tab = l2_pgentry_to_l1(*l2tab++) + l1_table_offset(vaddr);
for ( cur_address = alloc_address;
cur_address != end_address;
cur_address += PAGE_SIZE )
{
*l1tab++ = mk_l1_pgentry(l1_pgentry_val(*l1tab) & ~_PAGE_RW);
if ( !((unsigned long)l1tab & (PAGE_SIZE-1)) )
l1tab = l2_pgentry_to_l1(*l2tab++);
page = frame_table + (cur_address >> PAGE_SHIFT);
page->flags = dom | PGT_l1_page_table;
page->tot_count++;
}
page->flags = dom | PGT_l2_page_table;
/* Map in the the shared info structure. */
virt_shinfo_address = end_address - start_address + virt_load_address;
l2tab = pagetable_ptr(p->mm.pagetable) +
l2_table_offset(virt_shinfo_address);
l1tab = l2_pgentry_to_l1(*l2tab) +
l1_table_offset(virt_shinfo_address);
*l1tab = mk_l1_pgentry(__pa(p->shared_info)|L1_PROT);
/* Set up shared info area. */
rdtscll(time);
p->shared_info->wall_time = time;
p->shared_info->domain_time = time;
p->shared_info->ticks_per_ms = ticks_per_usec * 1000;
/* for DOM0, setup mapping of frame table */
if ( dom == 0 )
{
virt_ftable_start_addr = virt_load_address + virt_shinfo_address + PAGE_SIZE;
virt_ftable_end_addr = virt_ftable_start_addr + frame_table_size;
for(cur_address = virt_ftable_start_addr;
cur_address < virt_ftable_end_addr;
cur_address += PAGE_SIZE)
{
l2tab = pagetable_ptr(p->mm.pagetable) + l2_table_offset(cur_address);
l1tab = l2_pgentry_to_l1(*l2tab) + l1_table_offset(cur_address);
*l1tab = mk_l1_pgentry(__pa(cur_address)|L1_PROT);
}
}
virt_startinfo_address = (start_info_t *)
(alloc_address - start_address - PAGE_SIZE + virt_load_address);
virt_stack_address = (unsigned long)virt_startinfo_address;
/* Install the new page tables. */
__cli();
__asm__ __volatile__ (
"mov %%eax,%%cr3"
: : "a" (__pa(pagetable_ptr(p->mm.pagetable))));
/* Copy the guest OS image. */
src = (char *)__va(mod[0].mod_start + 12);
dst = (char *)virt_load_address;
while ( src < (char *)__va(mod[nr_mods-1].mod_end) ) *dst++ = *src++;
/* Set up start info area. */
memset(virt_startinfo_address, 0, sizeof(*virt_startinfo_address));
virt_startinfo_address->nr_pages = (end_address-start_address)>>PAGE_SHIFT;
virt_startinfo_address->shared_info =
(shared_info_t *)virt_shinfo_address;
virt_startinfo_address->pt_base =
end_address - PAGE_SIZE - start_address + virt_load_address;
virt_startinfo_address->phys_base = start_address;
/* NB. Next field will be NULL if dom != 0. */
virt_startinfo_address->frame_table = virt_ftable_start_addr;
/* Add virtual network interfaces and point to them in startinfo. */
while (params->num_vifs-- > 0) {
net_ring = create_net_vif(dom);
if (!net_ring) panic("no network ring!\n");
}
virt_startinfo_address->net_rings = p->net_ring_base;
virt_startinfo_address->num_net_rings = p->num_net_vifs;
/* Add block io interface */
virt_startinfo_address->blk_ring = p->blk_ring_base;
/* We tell OS about any modules we were given. */
if ( nr_mods > 1 )
{
virt_startinfo_address->mod_start =
(mod[1].mod_start-mod[0].mod_start-12) + virt_load_address;
virt_startinfo_address->mod_len =
mod[nr_mods-1].mod_end - mod[1].mod_start;
}
dst = virt_startinfo_address->cmd_line;
if ( mod[0].string )
{
char *modline = (char *)__va(mod[0].string);
for ( i = 0; i < 255; i++ )
{
if ( modline[i] == '\0' ) break;
*dst++ = modline[i];
}
}
*dst = '\0';
if ( opt_nfsroot )
{
unsigned char boot[150];
unsigned char ipbase[20], nfsserv[20], gateway[20], netmask[20];
unsigned char nfsroot[70];
snprintf(nfsroot, 70, opt_nfsroot, dom);
snprintf(boot, 200,
" root=/dev/nfs ip=%s:%s:%s:%s::eth0:off nfsroot=%s",
quad_to_str(opt_ipbase + dom, ipbase),
quad_to_str(opt_nfsserv, nfsserv),
quad_to_str(opt_gateway, gateway),
quad_to_str(opt_netmask, netmask),
nfsroot);
strcpy(dst, boot);
}
/* Reinstate the caller's page tables. */
__asm__ __volatile__ (
"mov %%eax,%%cr3"
: : "a" (__pa(pagetable_ptr(current->mm.pagetable))));
__sti();
new_thread(p,
(unsigned long)virt_load_address,
(unsigned long)virt_stack_address,
(unsigned long)virt_startinfo_address);
return 0;
}
void __init domain_init(void)
{
int i;
for ( i = 0; i < NR_CPUS; i++ )
{
INIT_LIST_HEAD(&schedule_data[i].runqueue);
spin_lock_init(&schedule_data[i].lock);
schedule_data[i].prev = &idle0_task;
schedule_data[i].curr = &idle0_task;
}
}
#if 0
unsigned long s = (mod[ 0].mod_start + (PAGE_SIZE-1)) & PAGE_MASK;
unsigned long e = (mod[nr_mods-1].mod_end + (PAGE_SIZE-1)) & PAGE_MASK;
while ( s != e )
{
free_pages((unsigned long)__va(s), 0);
s += PAGE_SIZE;
}
#endif
+33
View File
@@ -0,0 +1,33 @@
/******************************************************************************
* event.c
*
* A nice interface for passing per-domain asynchronous events.
* These events are handled in the hypervisor, prior to return
* to the guest OS.
*
* Copyright (c) 2002, K A Fraser
*/
#include <xeno/config.h>
#include <xeno/event.h>
typedef void (*hyp_event_callback_fn_t)(void);
extern void schedule(void);
extern void flush_rx_queue(void);
/* Ordering must match definitions of _HYP_EVENT_* in xeno/sched.h */
static hyp_event_callback_fn_t event_call_fn[] =
{
schedule,
flush_rx_queue,
kill_domain
};
/* Handle outstanding events for the currently-executing domain. */
void do_hyp_events(void)
{
int nr;
while ( (nr = ffs(current->hyp_events)) != 0 )
(event_call_fn[nr-1])();
}
+358
View File
@@ -0,0 +1,358 @@
#include <stdarg.h>
#include <xeno/lib.h>
#include <xeno/errno.h>
#include <xeno/multiboot.h>
#include <xeno/spinlock.h>
#include <xeno/sched.h>
#include <xeno/mm.h>
#include <xeno/delay.h>
#include <xeno/skbuff.h>
#include <xeno/interrupt.h>
#include <asm/io.h>
#include <asm/msr.h>
#include <asm/uaccess.h>
#include <xeno/dom0_ops.h>
/* VGA text definitions. */
#define COLUMNS 80
#define LINES 24
#define ATTRIBUTE 7
#define VIDEO __va(0xB8000)
static int xpos, ypos;
static volatile unsigned char *video;
spinlock_t console_lock = SPIN_LOCK_UNLOCKED;
struct e820entry {
unsigned long addr_lo, addr_hi; /* start of memory segment */
unsigned long size_lo, size_hi; /* size of memory segment */
unsigned long type; /* type of memory segment */
};
/* Used by domain.c:setup_guestos */
int nr_mods;
module_t *mod;
void init_serial(void);
void start_of_day(void);
/* Command line options and variables. */
unsigned long opt_ipbase=0, opt_nfsserv=0, opt_gateway=0, opt_netmask=0;
unsigned char opt_nfsroot[50]="";
unsigned int opt_dom0_mem = 16000; /* default kbytes for DOM0 */
enum { OPT_IP, OPT_STR, OPT_UINT };
static struct {
unsigned char *name;
int type;
void *var;
} opts[] = {
{ "ipbase", OPT_IP, &opt_ipbase },
{ "nfsserv", OPT_IP, &opt_nfsserv },
{ "gateway", OPT_IP, &opt_gateway },
{ "netmask", OPT_IP, &opt_netmask },
{ "nfsroot", OPT_STR, &opt_nfsroot },
{ "dom0_mem", OPT_UINT, &opt_dom0_mem },
{ NULL, 0, NULL }
};
void cmain (unsigned long magic, multiboot_info_t *mbi)
{
struct task_struct *new_dom;
dom0_newdomain_t dom0_params;
unsigned long max_page, remaining_hypervisor_memory;
unsigned char *cmdline;
int i;
init_serial();
cls();
if ( magic != MULTIBOOT_BOOTLOADER_MAGIC )
{
printf("Invalid magic number: 0x%x\n", (unsigned)magic);
return;
}
/*
* We require some kind of memory and module information.
* The rest we can fake!
*/
if ( (mbi->flags & 9) != 9 )
{
printf("Bad flags passed by bootloader: 0x%x\n", (unsigned)mbi->flags);
return;
}
if ( mbi->mods_count == 0 )
{
printf("Require at least one module!\n");
return;
}
/* Are mmap_* valid? */
#if 0
if ( (mbi->flags & (1<<6)) )
{
memory_map_t *mmap = (memory_map_t *)mbi->mmap_addr;
struct e820entry *e820 = E820_MAP;
while ( (unsigned long)mmap < (mbi->mmap_addr + mbi->mmap_length) )
{
e820->addr_lo = mmap->base_addr_low;
e820->addr_hi = mmap->base_addr_high;
e820->size_lo = mmap->length_low;
e820->size_hi = mmap->length_high;
e820->type = mmap->type;
e820++;
mmap = (memory_map_t *)
((unsigned long)mmap + mmap->size + sizeof (mmap->size));
}
}
#endif
nr_mods = mbi->mods_count;
mod = (module_t *)__va(mbi->mods_addr);
/* Parse the command line. */
cmdline = (unsigned char *)(mbi->cmdline ? __va(mbi->cmdline) : NULL);
if ( cmdline != NULL )
{
unsigned char *opt_end, *opt;
while ( *cmdline == ' ' ) cmdline++;
cmdline = strchr(cmdline, ' ');
while ( cmdline != NULL )
{
while ( *cmdline == ' ' ) cmdline++;
if ( (opt = strchr(cmdline, '=')) == NULL ) break;
*opt++ = '\0';
opt_end = strchr(opt, ' ');
if ( opt_end != NULL ) *opt_end++ = '\0';
for ( i = 0; opts[i].name != NULL; i++ )
{
if ( strcmp(opts[i].name, cmdline ) == 0 )
{
if ( opts[i].type == OPT_IP )
{
*(unsigned long *)opts[i].var = str_to_quad(opt);
}
else if(opts[i].type == OPT_STR)
{
strcpy(opts[i].var, opt);
}
else /* opts[i].type == OPT_UINT */
{
*(unsigned int *)opts[i].var = simple_strtol(opt, (char **)&opt, 10);
}
break;
}
}
cmdline = opt_end;
}
}
memcpy(&idle0_task_union, &first_task_struct, sizeof(first_task_struct));
max_page = (mbi->mem_upper+1024) >> (PAGE_SHIFT - 10);
if ( max_page > (MAX_USABLE_ADDRESS >> PAGE_SHIFT) )
max_page = MAX_USABLE_ADDRESS >> PAGE_SHIFT;
/* mem_upper is address of first memory hole in high memory, minus 1MB. */
/* PS. mem_upper is in kB. */
remaining_hypervisor_memory = init_frametable(max_page);
printk("Initialised %luMB of memory on a %luMB machine\n",
max_page >> (20-PAGE_SHIFT), (mbi->mem_upper>>10)+1);
init_page_allocator(mod[nr_mods-1].mod_end, remaining_hypervisor_memory);
/* These things will get done by do_newdomain() for all other tasks. */
current->shared_info = (void *)get_free_page(GFP_KERNEL);
memset(current->shared_info, 0, sizeof(shared_info_t));
set_fs(USER_DS);
current->num_net_vifs = 0;
start_of_day();
/* Create initial domain 0. */
dom0_params.num_vifs = 1;
dom0_params.memory_kb = opt_dom0_mem;
new_dom = do_newdomain();
if ( new_dom == NULL ) panic("Error creating domain 0\n");
new_dom->processor = 0;
new_dom->domain = 0;
if ( setup_guestos(new_dom, &dom0_params) != 0 )
{
panic("Could not set up DOM0 guest OS\n");
}
wake_up(new_dom);
cpu_idle();
}
#define SERIAL_BASE 0x3f8
#define RX_BUF 0
#define TX_HOLD 0
#define INT_ENABLE 1
#define INT_IDENT 2
#define DATA_FORMAT 3
#define LINE_CTL 4
#define LINE_STATUS 5
#define LINE_IN 6
#define DIVISOR_LO 0
#define DIVISOR_HI 1
void init_serial(void)
{
/* 9600 baud, no parity, 1 stop bit, 8 data bits. */
outb(0x83, SERIAL_BASE+DATA_FORMAT);
outb(12, SERIAL_BASE+DIVISOR_LO);
outb(0, SERIAL_BASE+DIVISOR_HI);
outb(0x03, SERIAL_BASE+DATA_FORMAT);
/* No interrupts. */
outb(0x00, SERIAL_BASE+INT_ENABLE);
}
void putchar_serial(unsigned char c)
{
if ( c == '\n' ) putchar_serial('\r');
if ( (c != '\n') && (c != '\r') && ((c < 32) || (c > 126)) ) return;
while ( !(inb(SERIAL_BASE+LINE_STATUS)&(1<<5)) ) barrier();
outb(c, SERIAL_BASE+TX_HOLD);
}
/* Clear the screen and initialize VIDEO, XPOS and YPOS. */
void cls (void)
{
int i;
video = (unsigned char *) VIDEO;
for (i = 0; i < COLUMNS * LINES * 2; i++)
*(video + i) = 0;
xpos = 0;
ypos = 0;
outw(10+(1<<(5+8)), 0x3d4); /* cursor off */
}
/* Put the character C on the screen. */
static void putchar (int c)
{
static char zeroarr[2*COLUMNS] = { 0 };
putchar_serial(c);
if (c == '\n' || c == '\r')
{
newline:
xpos = 0;
ypos++;
if (ypos >= LINES)
{
ypos = LINES-1;
memcpy((char*)video,
(char*)video + 2*COLUMNS, (LINES-1)*2*COLUMNS);
memcpy((char*)video + (LINES-1)*2*COLUMNS,
zeroarr, 2*COLUMNS);
}
return;
}
*(video + (xpos + ypos * COLUMNS) * 2) = c & 0xFF;
*(video + (xpos + ypos * COLUMNS) * 2 + 1) = ATTRIBUTE;
xpos++;
if (xpos >= COLUMNS)
goto newline;
}
static inline void __putstr(const char *str)
{
while ( *str ) putchar(*str++);
}
void printf (const char *fmt, ...)
{
va_list args;
char buf[1024], *p;
unsigned long flags;
va_start(args, fmt);
(void)vsnprintf(buf, sizeof(buf), fmt, args);
va_end(args);
p = buf;
spin_lock_irqsave(&console_lock, flags);
while ( *p ) putchar(*p++);
spin_unlock_irqrestore(&console_lock, flags);
}
void panic(const char *fmt, ...)
{
va_list args;
char buf[1024], *p;
unsigned long flags;
extern void machine_restart(char *);
va_start(args, fmt);
(void)vsnprintf(buf, sizeof(buf), fmt, args);
va_end(args);
/* Spit out multiline message in one go. */
spin_lock_irqsave(&console_lock, flags);
__putstr("\n****************************************\n");
p = buf;
while ( *p ) putchar(*p++);
__putstr("Aieee! CPU");
putchar((char)smp_processor_id() + '0');
__putstr(" is toast...\n");
__putstr("****************************************\n\n");
__putstr("Reboot in five seconds...\n");
spin_unlock_irqrestore(&console_lock, flags);
mdelay(5000);
machine_restart(0);
}
/* No-op syscall. */
asmlinkage long sys_ni_syscall(void)
{
return -ENOSYS;
}
long do_console_write(char *str, int count)
{
#define SIZEOF_BUF 256
unsigned char safe_str[SIZEOF_BUF];
unsigned long flags;
int i;
unsigned char prev = '\n';
if ( count > SIZEOF_BUF ) count = SIZEOF_BUF;
if ( copy_from_user(safe_str, str, count) )
return -EFAULT;
spin_lock_irqsave(&console_lock, flags);
for ( i = 0; i < count; i++ )
{
if ( prev == '\n' )
{
__putstr("DOM");
putchar(current->domain+'0');
__putstr(": ");
}
if ( !safe_str[i] ) break;
putchar(prev = safe_str[i]);
}
if ( prev != '\n' ) putchar('\n');
spin_unlock_irqrestore(&console_lock, flags);
return(0);
}
+171
View File
@@ -0,0 +1,171 @@
#include <xeno/ctype.h>
#include <xeno/lib.h>
int memcmp(const void * cs,const void * ct,size_t count)
{
const unsigned char *su1, *su2;
signed char res = 0;
for( su1 = cs, su2 = ct; 0 < count; ++su1, ++su2, count--)
if ((res = *su1 - *su2) != 0)
break;
return res;
}
void * memcpy(void * dest,const void *src,size_t count)
{
char *tmp = (char *) dest, *s = (char *) src;
while (count--)
*tmp++ = *s++;
return dest;
}
int strncmp(const char * cs,const char * ct,size_t count)
{
register signed char __res = 0;
while (count) {
if ((__res = *cs - *ct++) != 0 || !*cs++)
break;
count--;
}
return __res;
}
int strcmp(const char * cs,const char * ct)
{
register signed char __res;
while (1) {
if ((__res = *cs - *ct++) != 0 || !*cs++)
break;
}
return __res;
}
char * strcpy(char * dest,const char *src)
{
char *tmp = dest;
while ((*dest++ = *src++) != '\0')
/* nothing */;
return tmp;
}
char * strncpy(char * dest,const char *src,size_t count)
{
char *tmp = dest;
while (count-- && (*dest++ = *src++) != '\0')
/* nothing */;
return tmp;
}
void * memset(void * s,int c,size_t count)
{
char *xs = (char *) s;
while (count--)
*xs++ = c;
return s;
}
size_t strnlen(const char * s, size_t count)
{
const char *sc;
for (sc = s; count-- && *sc != '\0'; ++sc)
/* nothing */;
return sc - s;
}
size_t strlen(const char * s)
{
const char *sc;
for (sc = s; *sc != '\0'; ++sc)
/* nothing */;
return sc - s;
}
char * strchr(const char * s, int c)
{
for(; *s != (char) c; ++s)
if (*s == '\0')
return NULL;
return (char *) s;
}
char * strstr(const char * s1,const char * s2)
{
int l1, l2;
l2 = strlen(s2);
if (!l2)
return (char *) s1;
l1 = strlen(s1);
while (l1 >= l2) {
l1--;
if (!memcmp(s1,s2,l2))
return (char *) s1;
s1++;
}
return NULL;
}
/* for inc/ctype.h */
unsigned char _ctype[] = {
_C,_C,_C,_C,_C,_C,_C,_C, /* 0-7 */
_C,_C|_S,_C|_S,_C|_S,_C|_S,_C|_S,_C,_C, /* 8-15 */
_C,_C,_C,_C,_C,_C,_C,_C, /* 16-23 */
_C,_C,_C,_C,_C,_C,_C,_C, /* 24-31 */
_S|_SP,_P,_P,_P,_P,_P,_P,_P, /* 32-39 */
_P,_P,_P,_P,_P,_P,_P,_P, /* 40-47 */
_D,_D,_D,_D,_D,_D,_D,_D, /* 48-55 */
_D,_D,_P,_P,_P,_P,_P,_P, /* 56-63 */
_P,_U|_X,_U|_X,_U|_X,_U|_X,_U|_X,_U|_X,_U, /* 64-71 */
_U,_U,_U,_U,_U,_U,_U,_U, /* 72-79 */
_U,_U,_U,_U,_U,_U,_U,_U, /* 80-87 */
_U,_U,_U,_P,_P,_P,_P,_P, /* 88-95 */
_P,_L|_X,_L|_X,_L|_X,_L|_X,_L|_X,_L|_X,_L, /* 96-103 */
_L,_L,_L,_L,_L,_L,_L,_L, /* 104-111 */
_L,_L,_L,_L,_L,_L,_L,_L, /* 112-119 */
_L,_L,_L,_P,_P,_P,_P,_C, /* 120-127 */
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 128-143 */
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 144-159 */
_S|_SP,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P, /* 160-175 */
_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P, /* 176-191 */
_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U, /* 192-207 */
_U,_U,_U,_U,_U,_U,_U,_P,_U,_U,_U,_U,_U,_U,_U,_L, /* 208-223 */
_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L, /* 224-239 */
_L,_L,_L,_L,_L,_L,_L,_P,_L,_L,_L,_L,_L,_L,_L,_L}; /* 240-255 */
unsigned long str_to_quad(unsigned char *s)
{
unsigned long quad = 0;
do {
quad <<= 8;
quad |= simple_strtol(s, (char **)&s, 10);
}
while ( *s++ == '.' );
return quad;
}
unsigned char *quad_to_str(unsigned long q, unsigned char *s)
{
sprintf(s, "%ld.%ld.%ld.%ld",
(q>>24)&255, (q>>16)&255, (q>>8)&255, (q>>0)&255);
return s;
}
+654
View File
@@ -0,0 +1,654 @@
/******************************************************************************
* memory.c
*
* Copyright (c) 2002 K A Fraser
*
* A description of the page table API:
*
* Domains trap to process_page_updates with a list of update requests.
* This is a list of (ptr, val) pairs, where the requested operation
* is *ptr = val. The exceptions are when ptr is PGREQ_ADD_BASEPTR, or
* PGREQ_REMOVE_BASEPTR.
*
* Reference counting of pages:
* ----------------------------
* Each page has two refcounts: tot_count and type_count.
*
* TOT_COUNT is the obvious reference count. It counts all uses of a
* physical page frame by a domain, including uses as a page directory,
* a page table, or simple mappings via a PTE. This count prevents a
* domain from releasing a frame back to the hypervisor's free pool when
* it is still referencing it!
*
* TYPE_COUNT is more subtle. A frame can be put to one of three
* mutually-exclusive uses: it might be used as a page directory, or a
* page table, or it may be mapped writeable by the domain [of course, a
* frame may not be used in any of these three ways!].
* So, type_count is a count of the number of times a frame is being
* referred to in its current incarnation. Therefore, a page can only
* change its type when its type count is zero.
*
* A further note on writeable page mappings:
* ------------------------------------------
* For simplicity, the count of writeable mappings for a page may not
* correspond to reality. The 'writeable count' is incremented for every
* PTE which maps the page with the _PAGE_RW flag set. However, for
* write access to be possible the page directory entry must also have
* its _PAGE_RW bit set. We do not check this as it complicates the
* reference counting considerably [consider the case of multiple
* directory entries referencing a single page table, some with the RW
* bit set, others not -- it starts getting a bit messy].
* In normal use, this simplification shouldn't be a problem.
* However, the logic can be added if required.
*
* One more note on read-only page mappings:
* -----------------------------------------
* We want domains to be able to map pages for read-only access. The
* main reason is that page tables and directories should be readable
* by a domain, but it would not be safe for them to be writeable.
* However, domains have free access to rings 1 & 2 of the Intel
* privilege model. In terms of page protection, these are considered
* to be part of 'supervisor mode'. The WP bit in CR0 controls whether
* read-only restrictions are respected in supervisor mode -- if the
* bit is clear then any mapped page is writeable.
*
* We get round this by always setting the WP bit and disallowing
* updates to it. This is very unlikely to cause a problem for guest
* OS's, which will generally use the WP bit to simplify copy-on-write
* implementation (in that case, OS wants a fault when it writes to
* an application-supplied buffer).
*/
/*
* THE FOLLOWING ARE ISSUES IF GUEST OPERATING SYSTEMS BECOME SMP-CAPABLE.
* [THAT IS, THEY'RE NOT A PROBLEM NOW, AND MAY NOT EVER BE.]
* -----------------------------------------------------------------------
*
* *********
* UPDATE 15/7/02: Interface has changed --updates now specify physical
* address of page-table entry, rather than specifying a virtual address,
* so hypervisor no longer "walks" the page tables. Therefore the
* solution below cannot work. Another possibility is to add a new entry
* to our "struct page" which says to which top-level page table each
* lower-level page table or writeable mapping belongs. If it belongs to more
* than one, we'd probably just flush on all processors running the domain.
* *********
*
* ** 1 **
* The problem involves creating new page tables which might be mapped
* writeable in the TLB of another processor. As an example, a domain might be
* running in two contexts (ie. on two processors) simultaneously, using the
* same top-level page table in both contexts. Now, if context 1 sends an
* update request [make page P read-only, add a reference to page P as a page
* table], that will succeed if there was only one writeable mapping of P.
* However, that mapping may persist in the TLB of context 2.
*
* Solution: when installing a new page table, we must flush foreign TLBs as
* necessary. Naive solution is to flush on any processor running our domain.
* Cleverer solution is to flush on any processor running same top-level page
* table, but this will sometimes fail (consider two different top-level page
* tables which have a shared lower-level page table).
*
* A better solution: when squashing a write reference, check how many times
* that lowest-level table entry is referenced by ORing refcounts of tables
* down the page-table hierarchy. If results is != 1, we require flushing all
* instances of current domain if a new table is installed (because the
* lowest-level entry may be referenced by many top-level page tables).
* However, common case will be that result == 1, so we only need to flush
* processors with the same top-level page table. Make choice at
* table-installation time based on a `flush_level' flag, which is
* FLUSH_NONE, FLUSH_PAGETABLE, FLUSH_DOMAIN. A flush reduces this
* to FLUSH_NONE, while squashed write mappings can only promote up
* to more aggressive flush types.
*
* ** 2 **
* Same problem occurs when removing a page table, at level 1 say, then
* making it writeable. Need a TLB flush between otherwise another processor
* might write an illegal mapping into the old table, while yet another
* processor can use the illegal mapping because of a stale level-2 TLB
* entry. So, removal of a table reference sets 'flush_level' appropriately,
* and a flush occurs on next addition of a fresh write mapping.
*
* BETTER SOLUTION FOR BOTH 1 AND 2:
* When type_refcnt goes to zero, leave old type in place (don't set to
* PGT_none). Then, only flush if making a page table of a page with
* (cnt=0,type=PGT_writeable), or when adding a write mapping for a page
* with (cnt=0, type=PGT_pagexxx). A TLB flush will cause all pages
* with refcnt==0 to be reset to PGT_none. Need an array for the purpose,
* added to when a type_refcnt goes to zero, and emptied on a TLB flush.
* Either have per-domain table, or force TLB flush at end of each
* call to 'process_page_updates'.
* Most OSes will always keep a writeable reference hanging around, and
* page table structure is fairly static, so this mechanism should be
* fairly cheap.
*
* MAYBE EVEN BETTER? [somewhat dubious: not for first cut of the code]:
* If we need to force an intermediate flush, those other processors
* spin until we complete, then do a single TLB flush. They can spin on
* the lock protecting 'process_page_updates', and continue when that
* is freed. Saves cost of setting up and servicing an IPI: later
* communication is synchronous. Processors trying to install the domain
* or domain&pagetable would also enter the spin.
*
* ** 3 **
* Indeed, this problem generalises to reusing page tables at different
* levels of the hierarchy (conceptually, the guest OS can use the
* hypervisor to introduce illegal table entries by proxy). Consider
* unlinking a level-1 page table and reintroducing at level 2 with no
* TLB flush. Hypervisor can add a reference to some other level-1 table
* with the RW bit set. This is fine in the level-2 context, but some
* other processor may still be using that table in level-1 context
* (due to a stale TLB entry). At level 1 it may look like the
* processor has write access to the other level-1 page table! Therefore
* can add illegal values there with impunity :-(
*
* Fortunately, the solution above generalises to this extended problem.
*/
/*
* UPDATE 12.11.02.: We no longer have struct page and mem_map. These
* have been replaced by struct pfn_info and frame_table respectively.
*
* system_free_list is a list_head linking all system owned free pages.
* it is initialized in init_frametable.
*
* Boris Dragovic.
*/
#include <xeno/config.h>
#include <xeno/init.h>
#include <xeno/lib.h>
#include <xeno/mm.h>
#include <xeno/sched.h>
#include <xeno/errno.h>
#include <asm/page.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#if 1
#define MEM_LOG(_f, _a...) printk("DOM%d: (file=memory.c, line=%d) " _f "\n", current->domain, __LINE__, ## _a )
#else
#define MEM_LOG(_f, _a...) ((void)0)
#endif
/* 'get' checks parameter for validity before inc'ing refcnt. */
static int get_l2_table(unsigned long page_nr);
static int get_l1_table(unsigned long page_nr);
static int get_page(unsigned long page_nr, int writeable);
static int inc_page_refcnt(unsigned long page_nr, unsigned int type);
/* 'put' does no checking because if refcnt not zero, entity must be valid. */
static int put_l2_table(unsigned long page_nr);
static void put_l1_table(unsigned long page_nr);
static void put_page(unsigned long page_nr, int writeable);
static int dec_page_refcnt(unsigned long page_nr, unsigned int type);
static int mod_l2_entry(l2_pgentry_t *, l2_pgentry_t);
static int mod_l1_entry(l1_pgentry_t *, l1_pgentry_t);
/* frame table size and its size in pages */
frame_table_t * frame_table;
unsigned long frame_table_size;
unsigned long max_page;
struct list_head free_list;
unsigned int free_pfns;
/*
* init_frametable:
* Initialise per-frame memory information. The return value
* is the amount of memory available for use by the rest of Xen.
* The very highest frames are reserved for the per-frame info.
* This function should be called before initialising the
* page allocator!
*/
unsigned long __init init_frametable(unsigned long nr_pages)
{
struct pfn_info *pf;
unsigned long page_index;
max_page = nr_pages;
frame_table_size = nr_pages * sizeof(struct pfn_info);
frame_table_size = (frame_table_size + PAGE_SIZE - 1) & PAGE_MASK;
free_pfns = nr_pages - (MAX_MONITOR_ADDRESS >> PAGE_SHIFT);
frame_table = phys_to_virt(MAX_MONITOR_ADDRESS - frame_table_size);
memset(frame_table, 0, frame_table_size);
/* Put all domain-allocatable memory on a free list. */
INIT_LIST_HEAD(&free_list);
for( page_index = MAX_MONITOR_ADDRESS >> PAGE_SHIFT;
page_index < nr_pages;
page_index++ )
{
pf = list_entry(&frame_table[page_index].list, struct pfn_info, list);
list_add_tail(&pf->list, &free_list);
}
/* Return the remaing Xen-allocatable memory. */
return(MAX_MONITOR_ADDRESS - frame_table_size);
}
/* Return original refcnt, or -1 on error. */
static int inc_page_refcnt(unsigned long page_nr, unsigned int type)
{
struct pfn_info *page;
unsigned long flags;
if ( page_nr >= max_page )
{
MEM_LOG("Page out of range (%08lx>%08lx)", page_nr, max_page);
return(-1);
}
page = frame_table + page_nr;
flags = page->flags;
if ( (flags & PG_domain_mask) != current->domain )
{
MEM_LOG("Bad page domain (%ld)", flags & PG_domain_mask);
return(-1);
}
if ( (flags & PG_type_mask) != type )
{
if ( page_type_count(page) != 0 )
{
MEM_LOG("Page %08lx bad type/count (%08lx!=%08x) cnt=%ld",
page_nr << PAGE_SHIFT,
flags & PG_type_mask, type, page_type_count(page));
return(-1);
}
page->flags |= type;
}
get_page_tot(page);
return(get_page_type(page));
}
/* Return new refcnt, or -1 on error. */
static int dec_page_refcnt(unsigned long page_nr, unsigned int type)
{
struct pfn_info *page;
int ret;
if ( page_nr >= max_page )
{
MEM_LOG("Page out of range (%08lx>%08lx)", page_nr, max_page);
return(-1);
}
page = frame_table + page_nr;
if ( (page->flags & (PG_type_mask | PG_domain_mask)) !=
(type | current->domain) )
{
MEM_LOG("Bad page type/domain (dom=%ld) (type %ld != expected %d)",
page->flags & PG_domain_mask, page->flags & PG_type_mask,
type);
return(-1);
}
ASSERT(page_type_count(page) != 0);
if ( (ret = put_page_type(page)) == 0 ) page->flags &= ~PG_type_mask;
put_page_tot(page);
return(ret);
}
static int get_l2_table(unsigned long page_nr)
{
l2_pgentry_t *p_l2_entry, l2_entry;
int i, ret=0;
ret = inc_page_refcnt(page_nr, PGT_l2_page_table);
if ( ret != 0 ) return((ret < 0) ? ret : 0);
/* NEW level-2 page table! Deal with every PDE in the table. */
p_l2_entry = (l2_pgentry_t *)__va(page_nr << PAGE_SHIFT);
for ( i = 0; i < DOMAIN_ENTRIES_PER_L2_PAGETABLE; i++ )
{
l2_entry = *p_l2_entry++;
if ( !(l2_pgentry_val(l2_entry) & _PAGE_PRESENT) ) continue;
if ( (l2_pgentry_val(l2_entry) & (_PAGE_GLOBAL|_PAGE_PSE)) )
{
MEM_LOG("Bad L2 page type settings %04lx",
l2_pgentry_val(l2_entry) & (_PAGE_GLOBAL|_PAGE_PSE));
return(-1);
}
ret = get_l1_table(l2_pgentry_to_pagenr(l2_entry));
if ( ret ) return(ret);
}
/* Now we simply slap in our high mapping. */
memcpy(p_l2_entry, idle0_pg_table + DOMAIN_ENTRIES_PER_L2_PAGETABLE,
HYPERVISOR_ENTRIES_PER_L2_PAGETABLE * sizeof(l2_pgentry_t));
return(ret);
}
static int get_l1_table(unsigned long page_nr)
{
l1_pgentry_t *p_l1_entry, l1_entry;
int i, ret;
/* Update ref count for page pointed at by PDE. */
ret = inc_page_refcnt(page_nr, PGT_l1_page_table);
if ( ret != 0 ) return((ret < 0) ? ret : 0);
/* NEW level-1 page table! Deal with every PTE in the table. */
p_l1_entry = (l1_pgentry_t *)__va(page_nr << PAGE_SHIFT);
for ( i = 0; i < ENTRIES_PER_L1_PAGETABLE; i++ )
{
l1_entry = *p_l1_entry++;
if ( !(l1_pgentry_val(l1_entry) & _PAGE_PRESENT) ) continue;
if ( (l1_pgentry_val(l1_entry) &
(_PAGE_GLOBAL|_PAGE_PAT)) )
{
MEM_LOG("Bad L1 page type settings %04lx",
l1_pgentry_val(l1_entry) &
(_PAGE_GLOBAL|_PAGE_PAT));
return(-1);
}
ret = get_page(l1_pgentry_to_pagenr(l1_entry),
l1_pgentry_val(l1_entry) & _PAGE_RW);
if ( ret ) return(ret);
}
return(ret);
}
static int get_page(unsigned long page_nr, int writeable)
{
struct pfn_info *page;
unsigned long flags;
/* Update ref count for page pointed at by PTE. */
if ( page_nr >= max_page )
{
MEM_LOG("Page out of range (%08lx>%08lx)", page_nr, max_page);
return(-1);
}
page = frame_table + page_nr;
flags = page->flags;
if ( (flags & PG_domain_mask) != current->domain )
{
MEM_LOG("Bad page domain (%ld)", flags & PG_domain_mask);
return(-1);
}
if ( writeable )
{
if ( (flags & PG_type_mask) != PGT_writeable_page )
{
if ( page_type_count(page) != 0 )
{
MEM_LOG("Bad page type/count (%08lx!=%08x) cnt=%ld",
flags & PG_type_mask, PGT_writeable_page,
page_type_count(page));
return(-1);
}
page->flags |= PGT_writeable_page;
}
get_page_type(page);
}
get_page_tot(page);
return(0);
}
static int put_l2_table(unsigned long page_nr)
{
l2_pgentry_t *p_l2_entry, l2_entry;
int i, ret;
ret = dec_page_refcnt(page_nr, PGT_l2_page_table);
if ( ret != 0 ) return((ret < 0) ? ret : 0);
/* We had last reference to level-2 page table. Free the PDEs. */
p_l2_entry = (l2_pgentry_t *)__va(page_nr << PAGE_SHIFT);
for ( i = 0; i < HYPERVISOR_ENTRIES_PER_L2_PAGETABLE; i++ )
{
l2_entry = *p_l2_entry++;
if ( (l2_pgentry_val(l2_entry) & _PAGE_PRESENT) )
put_l1_table(l2_pgentry_to_pagenr(l2_entry));
}
return(0);
}
static void put_l1_table(unsigned long page_nr)
{
l1_pgentry_t *p_l1_entry, l1_entry;
int i;
if ( dec_page_refcnt(page_nr, PGT_l1_page_table) != 0 ) return;
/* We had last reference to level-1 page table. Free the PTEs. */
p_l1_entry = (l1_pgentry_t *)__va(page_nr << PAGE_SHIFT);
for ( i = 0; i < ENTRIES_PER_L1_PAGETABLE; i++ )
{
l1_entry = *p_l1_entry++;
if ( (l1_pgentry_val(l1_entry) & _PAGE_PRESENT) )
{
put_page(l1_pgentry_to_pagenr(l1_entry),
l1_pgentry_val(l1_entry) & _PAGE_RW);
}
}
}
static void put_page(unsigned long page_nr, int writeable)
{
struct pfn_info *page;
ASSERT(page_nr < max_page);
page = frame_table + page_nr;
ASSERT((page->flags & PG_domain_mask) == current->domain);
ASSERT((((page->flags & PG_type_mask) == PGT_writeable_page) &&
(page_type_count(page) != 0)) ||
(((page->flags & PG_type_mask) == PGT_none) &&
(page_type_count(page) == 0)));
ASSERT((!writeable) || (page_type_count(page) != 0));
if ( writeable && (put_page_type(page) == 0) )
page->flags &= ~PG_type_mask;
put_page_tot(page);
}
static int mod_l2_entry(l2_pgentry_t *p_l2_entry, l2_pgentry_t new_l2_entry)
{
l2_pgentry_t old_l2_entry = *p_l2_entry;
if ( (((unsigned long)p_l2_entry & (PAGE_SIZE-1)) >> 2) >=
DOMAIN_ENTRIES_PER_L2_PAGETABLE )
{
MEM_LOG("Illegal L2 update attempt in hypervisor area %p\n",
p_l2_entry);
goto fail;
}
if ( (l2_pgentry_val(new_l2_entry) & _PAGE_PRESENT) )
{
if ( (l2_pgentry_val(new_l2_entry) & (_PAGE_GLOBAL|_PAGE_PSE)) )
{
MEM_LOG("Bad L2 entry val %04lx",
l2_pgentry_val(new_l2_entry) &
(_PAGE_GLOBAL|_PAGE_PSE));
goto fail;
}
/* Differ in mapping (bits 12-31) or presence (bit 0)? */
if ( ((l2_pgentry_val(old_l2_entry) ^
l2_pgentry_val(new_l2_entry)) & 0xfffff001) != 0 )
{
if ( (l2_pgentry_val(old_l2_entry) & _PAGE_PRESENT) )
{
put_l1_table(l2_pgentry_to_pagenr(old_l2_entry));
}
if ( get_l1_table(l2_pgentry_to_pagenr(new_l2_entry)) )
goto fail;
}
}
else if ( (l2_pgentry_val(old_l2_entry) & _PAGE_PRESENT) )
{
put_l1_table(l2_pgentry_to_pagenr(old_l2_entry));
}
*p_l2_entry++ = new_l2_entry;
return(0);
fail:
return(-1);
}
static int mod_l1_entry(l1_pgentry_t *p_l1_entry, l1_pgentry_t new_l1_entry)
{
l1_pgentry_t old_l1_entry = *p_l1_entry;
if ( (l1_pgentry_val(new_l1_entry) & _PAGE_PRESENT) )
{
if ( (l1_pgentry_val(new_l1_entry) &
(_PAGE_GLOBAL|_PAGE_PAT)) )
{
MEM_LOG("Bad L1 entry val %04lx",
l1_pgentry_val(new_l1_entry) &
(_PAGE_GLOBAL|_PAGE_PAT));
goto fail;
}
/*
* Differ in mapping (bits 12-31), writeable (bit 1), or
* presence (bit 0)?
*/
if ( ((l1_pgentry_val(old_l1_entry) ^
l1_pgentry_val(new_l1_entry)) & 0xfffff003) != 0 )
{
if ( (l1_pgentry_val(old_l1_entry) & _PAGE_PRESENT) )
{
put_page(l1_pgentry_to_pagenr(old_l1_entry),
l1_pgentry_val(old_l1_entry) & _PAGE_RW);
}
if ( get_page(l1_pgentry_to_pagenr(new_l1_entry),
l1_pgentry_val(new_l1_entry) & _PAGE_RW) )
goto fail;
}
}
else if ( (l1_pgentry_val(old_l1_entry) & _PAGE_PRESENT) )
{
put_page(l1_pgentry_to_pagenr(old_l1_entry),
l1_pgentry_val(old_l1_entry) & _PAGE_RW);
}
*p_l1_entry++ = new_l1_entry;
return(0);
fail:
return(-1);
}
/* Apply updates to page table @pagetable_id within the current domain. */
int do_process_page_updates(page_update_request_t *updates, int count)
{
page_update_request_t cur;
unsigned long flags, pfn;
struct pfn_info *page;
int err = 0, i;
for ( i = 0; i < count; i++ )
{
if ( copy_from_user(&cur, updates, sizeof(cur)) )
{
kill_domain_with_errmsg("Cannot read page update request");
}
err = 1;
pfn = cur.ptr >> PAGE_SHIFT;
if ( !pfn )
{
switch ( cur.ptr )
{
case PGREQ_ADD_BASEPTR:
err = get_l2_table(cur.val >> PAGE_SHIFT);
break;
case PGREQ_REMOVE_BASEPTR:
if ( cur.val == __pa(pagetable_ptr(current->mm.pagetable)) )
{
MEM_LOG("Attempt to remove current baseptr! %08lx",
cur.val);
}
else
{
err = put_l2_table(cur.val >> PAGE_SHIFT);
}
break;
}
}
else if ( (cur.ptr & (sizeof(l1_pgentry_t)-1)) || (pfn >= max_page) )
{
MEM_LOG("Page out of range (%08lx>%08lx) or misalign %08lx",
pfn, max_page, cur.ptr);
}
else
{
page = frame_table + pfn;
flags = page->flags;
if ( (flags & PG_domain_mask) == current->domain )
{
switch ( (flags & PG_type_mask) )
{
case PGT_l1_page_table:
err = mod_l1_entry((l1_pgentry_t *)__va(cur.ptr),
mk_l1_pgentry(cur.val));
break;
case PGT_l2_page_table:
err = mod_l2_entry((l2_pgentry_t *)__va(cur.ptr),
mk_l2_pgentry(cur.val));
break;
}
}
}
if ( err )
{
kill_domain_with_errmsg("Illegal page update request");
}
updates++;
}
__asm__ __volatile__ ("movl %%eax,%%cr3" : :
"a" (__pa(pagetable_ptr(current->mm.pagetable))));
return(0);
}
int do_set_pagetable(unsigned long ptr)
{
struct pfn_info *page;
unsigned long pfn, flags;
if ( (ptr & ~PAGE_MASK) )
{
MEM_LOG("Misaligned new baseptr %08lx", ptr);
return -1;
}
pfn = ptr >> PAGE_SHIFT;
if ( pfn >= max_page )
{
MEM_LOG("Page out of range (%08lx>%08lx)", pfn, max_page);
return -1;
}
page = frame_table + (ptr >> PAGE_SHIFT);
flags = page->flags;
if ( (flags & (PG_domain_mask|PG_type_mask)) !=
(current->domain|PGT_l2_page_table) )
{
MEM_LOG("Page %08lx bad type/domain (dom=%ld) "
"(type %08lx != expected %08x)",
ptr, flags & PG_domain_mask, flags & PG_type_mask,
PGT_l2_page_table);
return -1;
}
current->mm.pagetable = mk_pagetable((unsigned long)__va(ptr));
__asm__ __volatile__ ("movl %%eax,%%cr3" : : "a" (ptr));
return 0;
}
+109
View File
@@ -0,0 +1,109 @@
/* net_ring.c
*
* ring data structures for buffering messages between hypervisor and
* guestos's. As it stands this is only used for network buffer exchange.
*
*/
#include <hypervisor-ifs/network.h>
#include <xeno/sched.h>
#include <xeno/errno.h>
#include <xeno/init.h>
#include <xeno/slab.h>
/* vif globals */
int sys_vif_count;
kmem_cache_t *net_vif_cache;
net_ring_t *create_net_vif(int domain)
{
net_vif_t *new_vif;
net_ring_t *new_ring;
struct task_struct *dom_task;
if ( !(dom_task = find_domain_by_id(domain)) )
{
return NULL;
}
if ( (new_vif = kmem_cache_alloc(net_vif_cache, GFP_KERNEL)) == NULL )
{
return NULL;
}
dom_task->net_vif_list[dom_task->num_net_vifs] = new_vif;
new_ring = dom_task->net_ring_base + dom_task->num_net_vifs;
memset(new_ring, 0, sizeof(net_ring_t));
dom_task->net_vif_list[dom_task->num_net_vifs]->net_ring = new_ring;
skb_queue_head_init(
&dom_task->net_vif_list[dom_task->num_net_vifs]->skb_list);
dom_task->net_vif_list[dom_task->num_net_vifs]->id = sys_vif_count++;
dom_task->num_net_vifs++;
return new_ring;
}
/* delete the last vif in the given domain. There doesn't seem to be any reason
* (yet) to be able to axe an arbitrary vif, by vif id.
*/
void destroy_net_vif(struct task_struct *p)
{
struct sk_buff *skb;
int i;
if ( p->num_net_vifs <= 0 ) return; // nothing to do.
i = --p->num_net_vifs;
while ( (skb = skb_dequeue(&p->net_vif_list[i]->skb_list)) != NULL )
{
kfree_skb(skb);
}
kmem_cache_free(net_vif_cache, p->net_vif_list[i]);
}
/*
* This is the hook function to handle guest-invoked traps requesting
* changes to the network system.
*/
long do_network_op(network_op_t *u_network_op)
{
long ret=0;
network_op_t op;
if ( current->domain != 0 )
return -EPERM;
if ( copy_from_user(&op, u_network_op, sizeof(op)) )
return -EFAULT;
switch ( op.cmd )
{
case NETWORK_OP_ADDRULE:
{
printk("received addrule request from guestos!\n");
}
break;
case NETWORK_OP_DELETERULE:
{
printk("received deleterule request from guestos!\n");
}
break;
default:
ret = -ENOSYS;
}
return ret;
}
void __init net_init (void)
{
sys_vif_count = 0;
net_vif_cache = kmem_cache_create("net_vif_cache", sizeof(net_vif_t),
0, SLAB_HWCACHE_ALIGN, NULL, NULL);
}
+272
View File
@@ -0,0 +1,272 @@
/******************************************************************************
* page_alloc.c
*
* Simple buddy allocator for Xenoserver hypervisor.
*
* Copyright (c) 2002 K A Fraser
*/
#include <xeno/config.h>
#include <xeno/init.h>
#include <xeno/types.h>
#include <xeno/lib.h>
#include <asm/page.h>
#include <xeno/spinlock.h>
static spinlock_t alloc_lock = SPIN_LOCK_UNLOCKED;
/*********************
* ALLOCATION BITMAP
* One bit per page of memory. Bit set => page is allocated.
*/
static unsigned long *alloc_bitmap;
#define PAGES_PER_MAPWORD (sizeof(unsigned long) * 8)
#define allocated_in_map(_pn) \
(alloc_bitmap[(_pn)/PAGES_PER_MAPWORD] & (1<<((_pn)&(PAGES_PER_MAPWORD-1))))
/*
* Hint regarding bitwise arithmetic in map_{alloc,free}:
* -(1<<n) sets all bits >= n.
* (1<<n)-1 sets all bits < n.
* Variable names in map_{alloc,free}:
* *_idx == Index into `alloc_bitmap' array.
* *_off == Bit offset within an element of the `alloc_bitmap' array.
*/
static void map_alloc(unsigned long first_page, unsigned long nr_pages)
{
unsigned long start_off, end_off, curr_idx, end_idx;
curr_idx = first_page / PAGES_PER_MAPWORD;
start_off = first_page & (PAGES_PER_MAPWORD-1);
end_idx = (first_page + nr_pages) / PAGES_PER_MAPWORD;
end_off = (first_page + nr_pages) & (PAGES_PER_MAPWORD-1);
if ( curr_idx == end_idx )
{
alloc_bitmap[curr_idx] |= ((1<<end_off)-1) & -(1<<start_off);
}
else
{
alloc_bitmap[curr_idx] |= -(1<<start_off);
while ( ++curr_idx < end_idx ) alloc_bitmap[curr_idx] = ~0L;
alloc_bitmap[curr_idx] |= (1<<end_off)-1;
}
}
static void map_free(unsigned long first_page, unsigned long nr_pages)
{
unsigned long start_off, end_off, curr_idx, end_idx;
curr_idx = first_page / PAGES_PER_MAPWORD;
start_off = first_page & (PAGES_PER_MAPWORD-1);
end_idx = (first_page + nr_pages) / PAGES_PER_MAPWORD;
end_off = (first_page + nr_pages) & (PAGES_PER_MAPWORD-1);
if ( curr_idx == end_idx )
{
alloc_bitmap[curr_idx] &= -(1<<end_off) | ((1<<start_off)-1);
}
else
{
alloc_bitmap[curr_idx] &= (1<<start_off)-1;
while ( ++curr_idx != end_idx ) alloc_bitmap[curr_idx] = 0;
alloc_bitmap[curr_idx] &= -(1<<end_off);
}
}
/*************************
* BINARY BUDDY ALLOCATOR
*/
typedef struct chunk_head_st chunk_head_t;
typedef struct chunk_tail_st chunk_tail_t;
struct chunk_head_st {
chunk_head_t *next;
chunk_head_t **pprev;
int level;
};
struct chunk_tail_st {
int level;
};
/* Linked lists of free chunks of different powers-of-two in size. */
#define FREELIST_SIZE ((sizeof(void*)<<3)-PAGE_SHIFT)
static chunk_head_t *free_list[FREELIST_SIZE];
static chunk_head_t free_tail[FREELIST_SIZE];
#define FREELIST_EMPTY(_l) ((_l)->next == NULL)
#define round_pgdown(_p) ((_p)&PAGE_MASK)
#define round_pgup(_p) (((_p)+(PAGE_SIZE-1))&PAGE_MASK)
/* Initialise allocator, placing addresses [@min,@max] in free pool. */
void __init init_page_allocator(unsigned long min, unsigned long max)
{
int i;
unsigned long range, bitmap_size;
chunk_head_t *ch;
chunk_tail_t *ct;
for ( i = 0; i < FREELIST_SIZE; i++ )
{
free_list[i] = &free_tail[i];
free_tail[i].pprev = &free_list[i];
free_tail[i].next = NULL;
}
min = round_pgup (min);
max = round_pgdown(max);
/* Allocate space for the allocation bitmap. */
bitmap_size = (max+1) >> (PAGE_SHIFT+3);
bitmap_size = round_pgup(bitmap_size);
alloc_bitmap = (unsigned long *)__va(min);
min += bitmap_size;
range = max - min;
/* All allocated by default. */
memset(alloc_bitmap, ~0, bitmap_size);
/* Free up the memory we've been given to play with. */
map_free(min>>PAGE_SHIFT, range>>PAGE_SHIFT);
/* The buddy lists are addressed in high memory. */
min += PAGE_OFFSET;
max += PAGE_OFFSET;
while ( range != 0 )
{
/*
* Next chunk is limited by alignment of min, but also
* must not be bigger than remaining range.
*/
for ( i = PAGE_SHIFT; (1<<(i+1)) <= range; i++ )
if ( min & (1<<i) ) break;
ch = (chunk_head_t *)min;
min += (1<<i);
range -= (1<<i);
ct = (chunk_tail_t *)min-1;
i -= PAGE_SHIFT;
ch->level = i;
ch->next = free_list[i];
ch->pprev = &free_list[i];
ch->next->pprev = &ch->next;
free_list[i] = ch;
ct->level = i;
}
}
/* Allocate 2^@order contiguous pages. */
unsigned long __get_free_pages(int mask, int order)
{
int i;
chunk_head_t *alloc_ch, *spare_ch;
chunk_tail_t *spare_ct;
unsigned long flags;
spin_lock_irqsave(&alloc_lock, flags);
/* Found smallest order which can satisfy the request. */
for ( i = order; FREELIST_EMPTY(free_list[i]); i++ )
{
if ( i == FREELIST_SIZE )
panic("Out of memory!\n");
}
/* Unlink a chunk. */
alloc_ch = free_list[i];
free_list[i] = alloc_ch->next;
alloc_ch->next->pprev = alloc_ch->pprev;
/* We may have to break the chunk a number of times. */
while ( i != order )
{
/* Split into two equal parts. */
i--;
spare_ch = (chunk_head_t *)((char *)alloc_ch + (1<<(i+PAGE_SHIFT)));
spare_ct = (chunk_tail_t *)((char *)spare_ch + (1<<(i+PAGE_SHIFT)))-1;
/* Create new header for spare chunk. */
spare_ch->level = i;
spare_ch->next = free_list[i];
spare_ch->pprev = &free_list[i];
spare_ct->level = i;
/* Link in the spare chunk. */
spare_ch->next->pprev = &spare_ch->next;
free_list[i] = spare_ch;
}
map_alloc(__pa(alloc_ch)>>PAGE_SHIFT, 1<<order);
spin_unlock_irqrestore(&alloc_lock, flags);
return((unsigned long)alloc_ch);
}
/* Free 2^@order pages at location @p. */
void __free_pages(unsigned long p, int order)
{
unsigned long size = 1 << (order + PAGE_SHIFT);
chunk_head_t *ch;
chunk_tail_t *ct;
unsigned long flags;
unsigned long pagenr = __pa(p) >> PAGE_SHIFT;
spin_lock_irqsave(&alloc_lock, flags);
map_free(pagenr, 1<<order);
/* Merge chunks as far as possible. */
for ( ; ; )
{
if ( (p & size) )
{
/* Merge with predecessor block? */
if ( allocated_in_map(pagenr-1) ) break;
ct = (chunk_tail_t *)p - 1;
if ( ct->level != order ) break;
ch = (chunk_head_t *)(p - size);
p -= size;
}
else
{
/* Merge with successor block? */
if ( allocated_in_map(pagenr+(1<<order)) ) break;
ch = (chunk_head_t *)(p + size);
if ( ch->level != order ) break;
}
/* Okay, unlink the neighbour. */
*ch->pprev = ch->next;
ch->next->pprev = ch->pprev;
order++;
size <<= 1;
}
/* Okay, add the final chunk to the appropriate free list. */
ch = (chunk_head_t *)p;
ct = (chunk_tail_t *)(p+size)-1;
ct->level = order;
ch->level = order;
ch->pprev = &free_list[order];
ch->next = free_list[order];
ch->next->pprev = &ch->next;
free_list[order] = ch;
spin_unlock_irqrestore(&alloc_lock, flags);
}
+329
View File
@@ -0,0 +1,329 @@
/*
* linux/kernel/resource.c
*
* Copyright (C) 1999 Linus Torvalds
* Copyright (C) 1999 Martin Mares <mj@ucw.cz>
*
* Arbitrary resource management.
*/
#include <linux/config.h>
#include <linux/lib.h>
#include <linux/sched.h>
#include <linux/errno.h>
#include <linux/ioport.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <asm/io.h>
struct resource ioport_resource = { "PCI IO", 0x0000, IO_SPACE_LIMIT, IORESOURCE_IO };
struct resource iomem_resource = { "PCI mem", 0x00000000, 0xffffffff, IORESOURCE_MEM };
static rwlock_t resource_lock = RW_LOCK_UNLOCKED;
/*
* This generates reports for /proc/ioports and /proc/iomem
*/
static char * do_resource_list(struct resource *entry, const char *fmt, int offset, char *buf, char *end)
{
if (offset < 0)
offset = 0;
while (entry) {
const char *name = entry->name;
unsigned long from, to;
if ((int) (end-buf) < 80)
return buf;
from = entry->start;
to = entry->end;
if (!name)
name = "<BAD>";
buf += sprintf(buf, fmt + offset, from, to, name);
if (entry->child)
buf = do_resource_list(entry->child, fmt, offset-2, buf, end);
entry = entry->sibling;
}
return buf;
}
int get_resource_list(struct resource *root, char *buf, int size)
{
char *fmt;
int retval;
fmt = " %08lx-%08lx : %s\n";
if (root->end < 0x10000)
fmt = " %04lx-%04lx : %s\n";
read_lock(&resource_lock);
retval = do_resource_list(root->child, fmt, 8, buf, buf + size) - buf;
read_unlock(&resource_lock);
return retval;
}
/* Return the conflict entry if you can't request it */
static struct resource * __request_resource(struct resource *root, struct resource *new)
{
unsigned long start = new->start;
unsigned long end = new->end;
struct resource *tmp, **p;
if (end < start)
return root;
if (start < root->start)
return root;
if (end > root->end)
return root;
p = &root->child;
for (;;) {
tmp = *p;
if (!tmp || tmp->start > end) {
new->sibling = tmp;
*p = new;
new->parent = root;
return NULL;
}
p = &tmp->sibling;
if (tmp->end < start)
continue;
return tmp;
}
}
static int __release_resource(struct resource *old)
{
struct resource *tmp, **p;
p = &old->parent->child;
for (;;) {
tmp = *p;
if (!tmp)
break;
if (tmp == old) {
*p = tmp->sibling;
old->parent = NULL;
return 0;
}
p = &tmp->sibling;
}
return -EINVAL;
}
int request_resource(struct resource *root, struct resource *new)
{
struct resource *conflict;
write_lock(&resource_lock);
conflict = __request_resource(root, new);
write_unlock(&resource_lock);
return conflict ? -EBUSY : 0;
}
int release_resource(struct resource *old)
{
int retval;
write_lock(&resource_lock);
retval = __release_resource(old);
write_unlock(&resource_lock);
return retval;
}
int check_resource(struct resource *root, unsigned long start, unsigned long len)
{
struct resource *conflict, tmp;
tmp.start = start;
tmp.end = start + len - 1;
write_lock(&resource_lock);
conflict = __request_resource(root, &tmp);
if (!conflict)
__release_resource(&tmp);
write_unlock(&resource_lock);
return conflict ? -EBUSY : 0;
}
/*
* Find empty slot in the resource tree given range and alignment.
*/
static int find_resource(struct resource *root, struct resource *new,
unsigned long size,
unsigned long min, unsigned long max,
unsigned long align,
void (*alignf)(void *, struct resource *, unsigned long),
void *alignf_data)
{
struct resource *this = root->child;
new->start = root->start;
for(;;) {
if (this)
new->end = this->start;
else
new->end = root->end;
if (new->start < min)
new->start = min;
if (new->end > max)
new->end = max;
new->start = (new->start + align - 1) & ~(align - 1);
if (alignf)
alignf(alignf_data, new, size);
if (new->start < new->end && new->end - new->start + 1 >= size) {
new->end = new->start + size - 1;
return 0;
}
if (!this)
break;
new->start = this->end + 1;
this = this->sibling;
}
return -EBUSY;
}
/*
* Allocate empty slot in the resource tree given range and alignment.
*/
int allocate_resource(struct resource *root, struct resource *new,
unsigned long size,
unsigned long min, unsigned long max,
unsigned long align,
void (*alignf)(void *, struct resource *, unsigned long),
void *alignf_data)
{
int err;
write_lock(&resource_lock);
err = find_resource(root, new, size, min, max, align, alignf, alignf_data);
if (err >= 0 && __request_resource(root, new))
err = -EBUSY;
write_unlock(&resource_lock);
return err;
}
/*
* This is compatibility stuff for IO resources.
*
* Note how this, unlike the above, knows about
* the IO flag meanings (busy etc).
*
* Request-region creates a new busy region.
*
* Check-region returns non-zero if the area is already busy
*
* Release-region releases a matching busy region.
*/
struct resource * __request_region(struct resource *parent, unsigned long start, unsigned long n, const char *name)
{
struct resource *res = kmalloc(sizeof(*res), GFP_KERNEL);
if (res) {
memset(res, 0, sizeof(*res));
res->name = name;
res->start = start;
res->end = start + n - 1;
res->flags = IORESOURCE_BUSY;
write_lock(&resource_lock);
for (;;) {
struct resource *conflict;
conflict = __request_resource(parent, res);
if (!conflict)
break;
if (conflict != parent) {
parent = conflict;
if (!(conflict->flags & IORESOURCE_BUSY))
continue;
}
/* Uhhuh, that didn't work out.. */
kfree(res);
res = NULL;
break;
}
write_unlock(&resource_lock);
}
return res;
}
int __check_region(struct resource *parent, unsigned long start, unsigned long n)
{
struct resource * res;
res = __request_region(parent, start, n, "check-region");
if (!res)
return -EBUSY;
release_resource(res);
kfree(res);
return 0;
}
void __release_region(struct resource *parent, unsigned long start, unsigned long n)
{
struct resource **p;
unsigned long end;
p = &parent->child;
end = start + n - 1;
for (;;) {
struct resource *res = *p;
if (!res)
break;
if (res->start <= start && res->end >= end) {
if (!(res->flags & IORESOURCE_BUSY)) {
p = &res->child;
continue;
}
if (res->start != start || res->end != end)
break;
*p = res->sibling;
kfree(res);
return;
}
p = &res->sibling;
}
printk("Trying to free nonexistent resource <%08lx-%08lx>\n", start, end);
}
#if 0
/*
* Called from init/main.c to reserve IO ports.
*/
#define MAXRESERVE 4
static int __init reserve_setup(char *str)
{
static int reserved = 0;
static struct resource reserve[MAXRESERVE];
for (;;) {
int io_start, io_num;
int x = reserved;
if (get_option (&str, &io_start) != 2)
break;
if (get_option (&str, &io_num) == 0)
break;
if (x < MAXRESERVE) {
struct resource *res = reserve + x;
res->name = "reserved";
res->start = io_start;
res->end = io_start + io_num - 1;
res->flags = IORESOURCE_BUSY;
res->child = NULL;
if (request_resource(res->start >= 0x10000 ? &iomem_resource : &ioport_resource, res) == 0)
reserved = x+1;
}
}
return 1;
}
__setup("reserve=", reserve_setup);
#endif
File diff suppressed because it is too large. Load diff
+334
View File
@@ -0,0 +1,334 @@
/*
* linux/kernel/softirq.c
*
* Copyright (C) 1992 Linus Torvalds
*
* Fixed a disable_bh()/enable_bh() race (was causing a console lockup)
* due bh_mask_count not atomic handling. Copyright (C) 1998 Andrea Arcangeli
*
* Rewritten. Old one was good in 2.2, but in 2.3 it was immoral. --ANK (990903)
*/
#include <linux/config.h>
#include <linux/mm.h>
#include <linux/sched.h>
//#include <linux/kernel_stat.h>
#include <linux/interrupt.h>
//#include <linux/smp_lock.h>
#include <linux/init.h>
#include <linux/tqueue.h>
/*
- No shared variables, all the data are CPU local.
- If a softirq needs serialization, let it serialize itself
by its own spinlocks.
- Even if softirq is serialized, only local cpu is marked for
execution. Hence, we get something sort of weak cpu binding.
Though it is still not clear, will it result in better locality
or will not.
- These softirqs are not masked by global cli() and start_bh_atomic()
(by clear reasons). Hence, old parts of code still using global locks
MUST NOT use softirqs, but insert interfacing routines acquiring
global locks. F.e. look at BHs implementation.
Examples:
- NET RX softirq. It is multithreaded and does not require
any global serialization.
- NET TX softirq. It kicks software netdevice queues, hence
it is logically serialized per device, but this serialization
is invisible to common code.
- Tasklets: serialized wrt itself.
- Bottom halves: globally serialized, grr...
*/
irq_cpustat_t irq_stat[NR_CPUS];
static struct softirq_action softirq_vec[32] __cacheline_aligned;
asmlinkage void do_softirq()
{
int cpu = smp_processor_id();
__u32 pending;
long flags;
if (in_interrupt())
return;
local_irq_save(flags);
pending = softirq_pending(cpu);
while (pending) {
struct softirq_action *h;
local_bh_disable();
restart:
/* Reset the pending bitmask before enabling irqs */
softirq_pending(cpu) = 0;
local_irq_enable();
h = softirq_vec;
do {
if (pending & 1)
h->action(h);
h++;
pending >>= 1;
} while (pending);
local_irq_disable();
pending = softirq_pending(cpu);
if (pending) goto restart;
__local_bh_enable();
}
local_irq_restore(flags);
}
/*
* This function must run with irq disabled!
*/
inline void cpu_raise_softirq(unsigned int cpu, unsigned int nr)
{
__cpu_raise_softirq(cpu, nr);
#ifdef CONFIG_SMP
if ( cpu != smp_processor_id() )
smp_send_event_check_cpu(cpu);
#endif
}
void raise_softirq(unsigned int nr)
{
long flags;
local_irq_save(flags);
cpu_raise_softirq(smp_processor_id(), nr);
local_irq_restore(flags);
}
void open_softirq(int nr, void (*action)(struct softirq_action*), void *data)
{
softirq_vec[nr].data = data;
softirq_vec[nr].action = action;
}
/* Tasklets */
struct tasklet_head tasklet_vec[NR_CPUS] __cacheline_aligned;
struct tasklet_head tasklet_hi_vec[NR_CPUS] __cacheline_aligned;
void __tasklet_schedule(struct tasklet_struct *t)
{
int cpu = smp_processor_id();
unsigned long flags;
local_irq_save(flags);
t->next = tasklet_vec[cpu].list;
tasklet_vec[cpu].list = t;
cpu_raise_softirq(cpu, TASKLET_SOFTIRQ);
local_irq_restore(flags);
}
void __tasklet_hi_schedule(struct tasklet_struct *t)
{
int cpu = smp_processor_id();
unsigned long flags;
local_irq_save(flags);
t->next = tasklet_hi_vec[cpu].list;
tasklet_hi_vec[cpu].list = t;
cpu_raise_softirq(cpu, HI_SOFTIRQ);
local_irq_restore(flags);
}
static void tasklet_action(struct softirq_action *a)
{
int cpu = smp_processor_id();
struct tasklet_struct *list;
local_irq_disable();
list = tasklet_vec[cpu].list;
tasklet_vec[cpu].list = NULL;
local_irq_enable();
while (list) {
struct tasklet_struct *t = list;
list = list->next;
if (tasklet_trylock(t)) {
if (!atomic_read(&t->count)) {
if (!test_and_clear_bit(TASKLET_STATE_SCHED, &t->state))
BUG();
t->func(t->data);
tasklet_unlock(t);
continue;
}
tasklet_unlock(t);
}
local_irq_disable();
t->next = tasklet_vec[cpu].list;
tasklet_vec[cpu].list = t;
__cpu_raise_softirq(cpu, TASKLET_SOFTIRQ);
local_irq_enable();
}
}
static void tasklet_hi_action(struct softirq_action *a)
{
int cpu = smp_processor_id();
struct tasklet_struct *list;
local_irq_disable();
list = tasklet_hi_vec[cpu].list;
tasklet_hi_vec[cpu].list = NULL;
local_irq_enable();
while (list) {
struct tasklet_struct *t = list;
list = list->next;
if (tasklet_trylock(t)) {
if (!atomic_read(&t->count)) {
if (!test_and_clear_bit(TASKLET_STATE_SCHED, &t->state))
BUG();
t->func(t->data);
tasklet_unlock(t);
continue;
}
tasklet_unlock(t);
}
local_irq_disable();
t->next = tasklet_hi_vec[cpu].list;
tasklet_hi_vec[cpu].list = t;
__cpu_raise_softirq(cpu, HI_SOFTIRQ);
local_irq_enable();
}
}
void tasklet_init(struct tasklet_struct *t,
void (*func)(unsigned long), unsigned long data)
{
t->next = NULL;
t->state = 0;
atomic_set(&t->count, 0);
t->func = func;
t->data = data;
}
void tasklet_kill(struct tasklet_struct *t)
{
if (in_interrupt())
printk("Attempt to kill tasklet from interrupt\n");
while (test_and_set_bit(TASKLET_STATE_SCHED, &t->state)) {
set_current_state(TASK_RUNNING);
do {
current->policy |= SCHED_YIELD;
schedule();
} while (test_bit(TASKLET_STATE_SCHED, &t->state));
}
tasklet_unlock_wait(t);
clear_bit(TASKLET_STATE_SCHED, &t->state);
}
/* Old style BHs */
static void (*bh_base[32])(void);
struct tasklet_struct bh_task_vec[32];
/* BHs are serialized by spinlock global_bh_lock.
It is still possible to make synchronize_bh() as
spin_unlock_wait(&global_bh_lock). This operation is not used
by kernel now, so that this lock is not made private only
due to wait_on_irq().
It can be removed only after auditing all the BHs.
*/
spinlock_t global_bh_lock = SPIN_LOCK_UNLOCKED;
static void bh_action(unsigned long nr)
{
int cpu = smp_processor_id();
if (!spin_trylock(&global_bh_lock))
goto resched;
if (!hardirq_trylock(cpu))
goto resched_unlock;
if (bh_base[nr])
bh_base[nr]();
hardirq_endlock(cpu);
spin_unlock(&global_bh_lock);
return;
resched_unlock:
spin_unlock(&global_bh_lock);
resched:
mark_bh(nr);
}
void init_bh(int nr, void (*routine)(void))
{
bh_base[nr] = routine;
mb();
}
void remove_bh(int nr)
{
tasklet_kill(bh_task_vec+nr);
bh_base[nr] = NULL;
}
void __init softirq_init()
{
int i;
for (i=0; i<32; i++)
tasklet_init(bh_task_vec+i, bh_action, i);
open_softirq(TASKLET_SOFTIRQ, tasklet_action, NULL);
open_softirq(HI_SOFTIRQ, tasklet_hi_action, NULL);
}
void __run_task_queue(task_queue *list)
{
struct list_head head, *next;
unsigned long flags;
spin_lock_irqsave(&tqueue_lock, flags);
list_add(&head, list);
list_del_init(list);
spin_unlock_irqrestore(&tqueue_lock, flags);
next = head.next;
while (next != &head) {
void (*f) (void *);
struct tq_struct *p;
void *data;
p = list_entry(next, struct tq_struct, list);
next = next->next;
f = p->routine;
data = p->data;
wmb();
p->sync = 0;
if (f)
f(data);
}
}
+621
View File
@@ -0,0 +1,621 @@
/*
* linux/kernel/timer.c
*
* Kernel internal timers, kernel timekeeping, basic process system calls
*
* Copyright (C) 1991, 1992 Linus Torvalds
*
* 1997-01-28 Modified by Finn Arne Gangstad to make timers scale better.
*
* 1997-09-10 Updated NTP code according to technical memorandum Jan '96
* "A Kernel Model for Precision Timekeeping" by Dave Mills
* 1998-12-24 Fixed a xtime SMP race (we need the xtime_lock rw spinlock to
* serialize accesses to xtime/lost_ticks).
* Copyright (C) 1998 Andrea Arcangeli
* 1999-03-10 Improved NTP compatibility by Ulrich Windl
*/
#include <linux/config.h>
#include <linux/mm.h>
#include <linux/time.h>
#include <linux/timer.h>
#include <linux/timex.h>
#include <linux/tqueue.h>
#include <linux/delay.h>
//#include <linux/smp_lock.h>
#include <linux/interrupt.h>
//#include <linux/kernel_stat.h>
#include <xeno/event.h>
#include <asm/uaccess.h>
/*
* Timekeeping variables
*/
long tick = (1000000 + HZ/2) / HZ; /* timer interrupt period */
/* The current time */
struct timeval xtime __attribute__ ((aligned (16)));
/* Don't completely fail for HZ > 500. */
int tickadj = 500/HZ ? : 1; /* microsecs */
DECLARE_TASK_QUEUE(tq_timer);
DECLARE_TASK_QUEUE(tq_immediate);
/*
* phase-lock loop variables
*/
/* TIME_ERROR prevents overwriting the CMOS clock */
int time_state = TIME_OK; /* clock synchronization status */
int time_status = STA_UNSYNC; /* clock status bits */
long time_offset; /* time adjustment (us) */
long time_constant = 2; /* pll time constant */
long time_tolerance = MAXFREQ; /* frequency tolerance (ppm) */
long time_precision = 1; /* clock precision (us) */
long time_maxerror = NTP_PHASE_LIMIT; /* maximum error (us) */
long time_esterror = NTP_PHASE_LIMIT; /* estimated error (us) */
long time_phase; /* phase offset (scaled us) */
long time_freq = ((1000000 + HZ/2) % HZ - HZ/2) << SHIFT_USEC;
/* frequency offset (scaled ppm)*/
long time_adj; /* tick adjust (scaled 1 / HZ) */
long time_reftime; /* time at last adjustment (s) */
long time_adjust;
long time_adjust_step;
unsigned long event;
unsigned long volatile jiffies;
unsigned int * prof_buffer;
unsigned long prof_len;
unsigned long prof_shift;
/*
* Event timer code
*/
#define TVN_BITS 6
#define TVR_BITS 8
#define TVN_SIZE (1 << TVN_BITS)
#define TVR_SIZE (1 << TVR_BITS)
#define TVN_MASK (TVN_SIZE - 1)
#define TVR_MASK (TVR_SIZE - 1)
struct timer_vec {
int index;
struct list_head vec[TVN_SIZE];
};
struct timer_vec_root {
int index;
struct list_head vec[TVR_SIZE];
};
static struct timer_vec tv5;
static struct timer_vec tv4;
static struct timer_vec tv3;
static struct timer_vec tv2;
static struct timer_vec_root tv1;
static struct timer_vec * const tvecs[] = {
(struct timer_vec *)&tv1, &tv2, &tv3, &tv4, &tv5
};
#define NOOF_TVECS (sizeof(tvecs) / sizeof(tvecs[0]))
void init_timervecs (void)
{
int i;
for (i = 0; i < TVN_SIZE; i++) {
INIT_LIST_HEAD(tv5.vec + i);
INIT_LIST_HEAD(tv4.vec + i);
INIT_LIST_HEAD(tv3.vec + i);
INIT_LIST_HEAD(tv2.vec + i);
}
for (i = 0; i < TVR_SIZE; i++)
INIT_LIST_HEAD(tv1.vec + i);
}
static unsigned long timer_jiffies;
static inline void internal_add_timer(struct timer_list *timer)
{
/*
* must be cli-ed when calling this
*/
unsigned long expires = timer->expires;
unsigned long idx = expires - timer_jiffies;
struct list_head * vec;
if (idx < TVR_SIZE) {
int i = expires & TVR_MASK;
vec = tv1.vec + i;
} else if (idx < 1 << (TVR_BITS + TVN_BITS)) {
int i = (expires >> TVR_BITS) & TVN_MASK;
vec = tv2.vec + i;
} else if (idx < 1 << (TVR_BITS + 2 * TVN_BITS)) {
int i = (expires >> (TVR_BITS + TVN_BITS)) & TVN_MASK;
vec = tv3.vec + i;
} else if (idx < 1 << (TVR_BITS + 3 * TVN_BITS)) {
int i = (expires >> (TVR_BITS + 2 * TVN_BITS)) & TVN_MASK;
vec = tv4.vec + i;
} else if ((signed long) idx < 0) {
/* can happen if you add a timer with expires == jiffies,
* or you set a timer to go off in the past
*/
vec = tv1.vec + tv1.index;
} else if (idx <= 0xffffffffUL) {
int i = (expires >> (TVR_BITS + 3 * TVN_BITS)) & TVN_MASK;
vec = tv5.vec + i;
} else {
/* Can only get here on architectures with 64-bit jiffies */
INIT_LIST_HEAD(&timer->list);
return;
}
/*
* Timers are FIFO!
*/
list_add(&timer->list, vec->prev);
}
/* Initialize both explicitly - let's try to have them in the same cache line */
spinlock_t timerlist_lock = SPIN_LOCK_UNLOCKED;
#ifdef CONFIG_SMP
volatile struct timer_list * volatile running_timer;
#define timer_enter(t) do { running_timer = t; mb(); } while (0)
#define timer_exit() do { running_timer = NULL; } while (0)
#define timer_is_running(t) (running_timer == t)
#define timer_synchronize(t) while (timer_is_running(t)) barrier()
#else
#define timer_enter(t) do { } while (0)
#define timer_exit() do { } while (0)
#endif
void add_timer(struct timer_list *timer)
{
unsigned long flags;
spin_lock_irqsave(&timerlist_lock, flags);
if (timer_pending(timer))
goto bug;
internal_add_timer(timer);
spin_unlock_irqrestore(&timerlist_lock, flags);
return;
bug:
spin_unlock_irqrestore(&timerlist_lock, flags);
printk("bug: kernel timer added twice at %p.\n",
__builtin_return_address(0));
}
static inline int detach_timer (struct timer_list *timer)
{
if (!timer_pending(timer))
return 0;
list_del(&timer->list);
return 1;
}
int mod_timer(struct timer_list *timer, unsigned long expires)
{
int ret;
unsigned long flags;
spin_lock_irqsave(&timerlist_lock, flags);
timer->expires = expires;
ret = detach_timer(timer);
internal_add_timer(timer);
spin_unlock_irqrestore(&timerlist_lock, flags);
return ret;
}
int del_timer(struct timer_list * timer)
{
int ret;
unsigned long flags;
spin_lock_irqsave(&timerlist_lock, flags);
ret = detach_timer(timer);
timer->list.next = timer->list.prev = NULL;
spin_unlock_irqrestore(&timerlist_lock, flags);
return ret;
}
#ifdef CONFIG_SMP
void sync_timers(void)
{
spin_unlock_wait(&global_bh_lock);
}
/*
* SMP specific function to delete periodic timer.
* Caller must disable by some means restarting the timer
* for new. Upon exit the timer is not queued and handler is not running
* on any CPU. It returns number of times, which timer was deleted
* (for reference counting).
*/
int del_timer_sync(struct timer_list * timer)
{
int ret = 0;
for (;;) {
unsigned long flags;
int running;
spin_lock_irqsave(&timerlist_lock, flags);
ret += detach_timer(timer);
timer->list.next = timer->list.prev = 0;
running = timer_is_running(timer);
spin_unlock_irqrestore(&timerlist_lock, flags);
if (!running)
break;
timer_synchronize(timer);
}
return ret;
}
#endif
static inline void cascade_timers(struct timer_vec *tv)
{
/* cascade all the timers from tv up one level */
struct list_head *head, *curr, *next;
head = tv->vec + tv->index;
curr = head->next;
/*
* We are removing _all_ timers from the list, so we don't have to
* detach them individually, just clear the list afterwards.
*/
while (curr != head) {
struct timer_list *tmp;
tmp = list_entry(curr, struct timer_list, list);
next = curr->next;
list_del(curr); // not needed
internal_add_timer(tmp);
curr = next;
}
INIT_LIST_HEAD(head);
tv->index = (tv->index + 1) & TVN_MASK;
}
static inline void run_timer_list(void)
{
spin_lock_irq(&timerlist_lock);
while ((long)(jiffies - timer_jiffies) >= 0) {
struct list_head *head, *curr;
if (!tv1.index) {
int n = 1;
do {
cascade_timers(tvecs[n]);
} while (tvecs[n]->index == 1 && ++n < NOOF_TVECS);
}
repeat:
head = tv1.vec + tv1.index;
curr = head->next;
if (curr != head) {
struct timer_list *timer;
void (*fn)(unsigned long);
unsigned long data;
timer = list_entry(curr, struct timer_list, list);
fn = timer->function;
data= timer->data;
detach_timer(timer);
timer->list.next = timer->list.prev = NULL;
timer_enter(timer);
spin_unlock_irq(&timerlist_lock);
fn(data);
spin_lock_irq(&timerlist_lock);
timer_exit();
goto repeat;
}
++timer_jiffies;
tv1.index = (tv1.index + 1) & TVR_MASK;
}
spin_unlock_irq(&timerlist_lock);
}
spinlock_t tqueue_lock = SPIN_LOCK_UNLOCKED;
void tqueue_bh(void)
{
run_task_queue(&tq_timer);
}
void immediate_bh(void)
{
run_task_queue(&tq_immediate);
}
/*
* this routine handles the overflow of the microsecond field
*
* The tricky bits of code to handle the accurate clock support
* were provided by Dave Mills (Mills@UDEL.EDU) of NTP fame.
* They were originally developed for SUN and DEC kernels.
* All the kudos should go to Dave for this stuff.
*
*/
static void second_overflow(void)
{
long ltemp;
/* Bump the maxerror field */
time_maxerror += time_tolerance >> SHIFT_USEC;
if ( time_maxerror > NTP_PHASE_LIMIT ) {
time_maxerror = NTP_PHASE_LIMIT;
time_status |= STA_UNSYNC;
}
/*
* Leap second processing. If in leap-insert state at
* the end of the day, the system clock is set back one
* second; if in leap-delete state, the system clock is
* set ahead one second. The microtime() routine or
* external clock driver will insure that reported time
* is always monotonic. The ugly divides should be
* replaced.
*/
switch (time_state) {
case TIME_OK:
if (time_status & STA_INS)
time_state = TIME_INS;
else if (time_status & STA_DEL)
time_state = TIME_DEL;
break;
case TIME_INS:
if (xtime.tv_sec % 86400 == 0) {
xtime.tv_sec--;
time_state = TIME_OOP;
printk(KERN_NOTICE "Clock: inserting leap second 23:59:60 UTC\n");
}
break;
case TIME_DEL:
if ((xtime.tv_sec + 1) % 86400 == 0) {
xtime.tv_sec++;
time_state = TIME_WAIT;
printk(KERN_NOTICE "Clock: deleting leap second 23:59:59 UTC\n");
}
break;
case TIME_OOP:
time_state = TIME_WAIT;
break;
case TIME_WAIT:
if (!(time_status & (STA_INS | STA_DEL)))
time_state = TIME_OK;
}
/*
* Compute the phase adjustment for the next second. In
* PLL mode, the offset is reduced by a fixed factor
* times the time constant. In FLL mode the offset is
* used directly. In either mode, the maximum phase
* adjustment for each second is clamped so as to spread
* the adjustment over not more than the number of
* seconds between updates.
*/
if (time_offset < 0) {
ltemp = -time_offset;
if (!(time_status & STA_FLL))
ltemp >>= SHIFT_KG + time_constant;
if (ltemp > (MAXPHASE / MINSEC) << SHIFT_UPDATE)
ltemp = (MAXPHASE / MINSEC) << SHIFT_UPDATE;
time_offset += ltemp;
time_adj = -ltemp << (SHIFT_SCALE - SHIFT_HZ - SHIFT_UPDATE);
} else {
ltemp = time_offset;
if (!(time_status & STA_FLL))
ltemp >>= SHIFT_KG + time_constant;
if (ltemp > (MAXPHASE / MINSEC) << SHIFT_UPDATE)
ltemp = (MAXPHASE / MINSEC) << SHIFT_UPDATE;
time_offset -= ltemp;
time_adj = ltemp << (SHIFT_SCALE - SHIFT_HZ - SHIFT_UPDATE);
}
if (ltemp < 0)
time_adj -= -ltemp >>
(SHIFT_USEC + SHIFT_HZ - SHIFT_SCALE);
else
time_adj += ltemp >>
(SHIFT_USEC + SHIFT_HZ - SHIFT_SCALE);
#if HZ == 100
/* Compensate for (HZ==100) != (1 << SHIFT_HZ).
* Add 25% and 3.125% to get 128.125; => only 0.125% error (p. 14)
*/
if (time_adj < 0)
time_adj -= (-time_adj >> 2) + (-time_adj >> 5);
else
time_adj += (time_adj >> 2) + (time_adj >> 5);
#endif
}
/* in the NTP reference this is called "hardclock()" */
static void update_wall_time_one_tick(void)
{
if ( (time_adjust_step = time_adjust) != 0 ) {
/* We are doing an adjtime thing.
*
* Prepare time_adjust_step to be within bounds.
* Note that a positive time_adjust means we want the clock
* to run faster.
*
* Limit the amount of the step to be in the range
* -tickadj .. +tickadj
*/
if (time_adjust > tickadj)
time_adjust_step = tickadj;
else if (time_adjust < -tickadj)
time_adjust_step = -tickadj;
/* Reduce by this step the amount of time left */
time_adjust -= time_adjust_step;
}
xtime.tv_usec += tick + time_adjust_step;
/*
* Advance the phase, once it gets to one microsecond, then
* advance the tick more.
*/
time_phase += time_adj;
if (time_phase <= -FINEUSEC) {
long ltemp = -time_phase >> SHIFT_SCALE;
time_phase += ltemp << SHIFT_SCALE;
xtime.tv_usec -= ltemp;
}
else if (time_phase >= FINEUSEC) {
long ltemp = time_phase >> SHIFT_SCALE;
time_phase -= ltemp << SHIFT_SCALE;
xtime.tv_usec += ltemp;
}
}
/*
* Using a loop looks inefficient, but "ticks" is
* usually just one (we shouldn't be losing ticks,
* we're doing this this way mainly for interrupt
* latency reasons, not because we think we'll
* have lots of lost timer ticks
*/
static void update_wall_time(unsigned long ticks)
{
do {
ticks--;
update_wall_time_one_tick();
} while (ticks);
if (xtime.tv_usec >= 1000000) {
xtime.tv_usec -= 1000000;
xtime.tv_sec++;
second_overflow();
}
}
static inline void do_process_times(struct task_struct *p,
unsigned long user, unsigned long system)
{
//unsigned long psecs;
// psecs = (p->times.tms_utime += user);
//psecs += (p->times.tms_stime += system);
}
void update_one_process(struct task_struct *p, unsigned long user,
unsigned long system, int cpu)
{
// p->per_cpu_utime[cpu] += user;
// p->per_cpu_stime[cpu] += system;
do_process_times(p, user, system);
}
/*
* Called from the timer interrupt handler to charge one tick to the current
* process. user_tick is 1 if the tick is user time, 0 for system.
*/
void update_process_times(int user_tick)
{
struct task_struct *p = current;
int cpu = smp_processor_id(), system = user_tick ^ 1;
update_one_process(p, user_tick, system, cpu);
if ( --p->counter <= 0 )
{
p->counter = 0;
set_bit(_HYP_EVENT_NEED_RESCHED, &p->hyp_events);
}
}
/* jiffies at the most recent update of wall time */
unsigned long wall_jiffies;
/*
* This spinlock protect us from races in SMP while playing with xtime. -arca
*/
rwlock_t xtime_lock = RW_LOCK_UNLOCKED;
static inline void update_times(void)
{
unsigned long ticks;
/*
* update_times() is run from the raw timer_bh handler so we
* just know that the irqs are locally enabled and so we don't
* need to save/restore the flags of the local CPU here. -arca
*/
write_lock_irq(&xtime_lock);
ticks = jiffies - wall_jiffies;
if (ticks) {
wall_jiffies += ticks;
update_wall_time(ticks);
}
write_unlock_irq(&xtime_lock);
}
void timer_bh(void)
{
update_times();
run_timer_list();
}
#include <xeno/errno.h>
#include <xeno/sched.h>
#include <xeno/lib.h>
#include <xeno/config.h>
#include <xeno/smp.h>
#include <xeno/irq.h>
#include <asm/msr.h>
void do_timer(struct pt_regs *regs)
{
struct task_struct *p;
shared_info_t *s;
unsigned long long wall;
unsigned long cpu_mask = 0;
(*(unsigned long *)&jiffies)++;
if ( !using_apic_timer )
update_process_times(user_mode(regs));
rdtscll(wall);
read_lock(&tasklist_lock);
p = &idle0_task;
do {
s = p->shared_info;
s->wall_time = s->domain_time = wall;
cpu_mask |= mark_guest_event(p, _EVENT_TIMER);
}
while ( (p = p->next_task) != &idle0_task );
read_unlock(&tasklist_lock);
guest_event_notify(cpu_mask);
mark_bh(TIMER_BH);
if (TQ_ACTIVE(tq_timer))
mark_bh(TQUEUE_BH);
}
void get_fast_time(struct timeval * tm)
{
*tm=xtime;
}
+713
View File
@@ -0,0 +1,713 @@
/*
* linux/lib/vsprintf.c
*
* Copyright (C) 1991, 1992 Linus Torvalds
*/
/* vsprintf.c -- Lars Wirzenius & Linus Torvalds. */
/*
* Wirzenius wrote this portably, Torvalds fucked it up :-)
*/
/*
* Fri Jul 13 2001 Crutcher Dunnavant <crutcher+kernel@datastacks.com>
* - changed to provide snprintf and vsnprintf functions
*/
#include <stdarg.h>
#include <xeno/ctype.h>
#include <xeno/lib.h>
/**
* simple_strtoul - convert a string to an unsigned long
* @cp: The start of the string
* @endp: A pointer to the end of the parsed string will be placed here
* @base: The number base to use
*/
unsigned long simple_strtoul(const char *cp,char **endp,unsigned int base)
{
unsigned long result = 0,value;
if (!base) {
base = 10;
if (*cp == '0') {
base = 8;
cp++;
if ((*cp == 'x') && isxdigit(cp[1])) {
cp++;
base = 16;
}
}
}
while (isxdigit(*cp) &&
(value = isdigit(*cp) ? *cp-'0' : toupper(*cp)-'A'+10) < base) {
result = result*base + value;
cp++;
}
if (endp)
*endp = (char *)cp;
return result;
}
/**
* simple_strtol - convert a string to a signed long
* @cp: The start of the string
* @endp: A pointer to the end of the parsed string will be placed here
* @base: The number base to use
*/
long simple_strtol(const char *cp,char **endp,unsigned int base)
{
if(*cp=='-')
return -simple_strtoul(cp+1,endp,base);
return simple_strtoul(cp,endp,base);
}
/**
* simple_strtoull - convert a string to an unsigned long long
* @cp: The start of the string
* @endp: A pointer to the end of the parsed string will be placed here
* @base: The number base to use
*/
unsigned long long simple_strtoull(const char *cp,char **endp,unsigned int base)
{
unsigned long long result = 0,value;
if (!base) {
base = 10;
if (*cp == '0') {
base = 8;
cp++;
if ((*cp == 'x') && isxdigit(cp[1])) {
cp++;
base = 16;
}
}
}
while (isxdigit(*cp) && (value = isdigit(*cp) ? *cp-'0' : (islower(*cp)
? toupper(*cp) : *cp)-'A'+10) < base) {
result = result*base + value;
cp++;
}
if (endp)
*endp = (char *)cp;
return result;
}
/**
* simple_strtoll - convert a string to a signed long long
* @cp: The start of the string
* @endp: A pointer to the end of the parsed string will be placed here
* @base: The number base to use
*/
long long simple_strtoll(const char *cp,char **endp,unsigned int base)
{
if(*cp=='-')
return -simple_strtoull(cp+1,endp,base);
return simple_strtoull(cp,endp,base);
}
static int skip_atoi(const char **s)
{
int i=0;
while (isdigit(**s))
i = i*10 + *((*s)++) - '0';
return i;
}
#define ZEROPAD 1 /* pad with zero */
#define SIGN 2 /* unsigned/signed long */
#define PLUS 4 /* show plus */
#define SPACE 8 /* space if plus */
#define LEFT 16 /* left justified */
#define SPECIAL 32 /* 0x */
#define LARGE 64 /* use 'ABCDEF' instead of 'abcdef' */
static char * number(char * buf, char * end, long num, int base, int size, int precision, int type)
{
char c,sign,tmp[66];
const char *digits;
const char small_digits[] = "0123456789abcdefghijklmnopqrstuvwxyz";
const char large_digits[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
int i;
digits = (type & LARGE) ? large_digits : small_digits;
if (type & LEFT)
type &= ~ZEROPAD;
if (base < 2 || base > 36)
return buf;
c = (type & ZEROPAD) ? '0' : ' ';
sign = 0;
if (type & SIGN) {
if (num < 0) {
sign = '-';
num = -num;
size--;
} else if (type & PLUS) {
sign = '+';
size--;
} else if (type & SPACE) {
sign = ' ';
size--;
}
}
if (type & SPECIAL) {
if (base == 16)
size -= 2;
else if (base == 8)
size--;
}
i = 0;
if (num == 0)
tmp[i++]='0';
else
{
/* XXX KAF: force unsigned mod and div. */
unsigned long num2=(unsigned long)num;
unsigned int base2=(unsigned int)base;
while (num2 != 0) { tmp[i++] = digits[num2%base2]; num2 /= base2; }
}
if (i > precision)
precision = i;
size -= precision;
if (!(type&(ZEROPAD+LEFT))) {
while(size-->0) {
if (buf <= end)
*buf = ' ';
++buf;
}
}
if (sign) {
if (buf <= end)
*buf = sign;
++buf;
}
if (type & SPECIAL) {
if (base==8) {
if (buf <= end)
*buf = '0';
++buf;
} else if (base==16) {
if (buf <= end)
*buf = '0';
++buf;
if (buf <= end)
*buf = digits[33];
++buf;
}
}
if (!(type & LEFT)) {
while (size-- > 0) {
if (buf <= end)
*buf = c;
++buf;
}
}
while (i < precision--) {
if (buf <= end)
*buf = '0';
++buf;
}
while (i-- > 0) {
if (buf <= end)
*buf = tmp[i];
++buf;
}
while (size-- > 0) {
if (buf <= end)
*buf = ' ';
++buf;
}
return buf;
}
/**
* vsnprintf - Format a string and place it in a buffer
* @buf: The buffer to place the result into
* @size: The size of the buffer, including the trailing null space
* @fmt: The format string to use
* @args: Arguments for the format string
*
* Call this function if you are already dealing with a va_list.
* You probably want snprintf instead.
*/
int vsnprintf(char *buf, size_t size, const char *fmt, va_list args)
{
int len;
unsigned long long num;
int i, base;
char *str, *end, c;
const char *s;
int flags; /* flags to number() */
int field_width; /* width of output field */
int precision; /* min. # of digits for integers; max
number of chars for from string */
int qualifier; /* 'h', 'l', or 'L' for integer fields */
/* 'z' support added 23/7/1999 S.H. */
/* 'z' changed to 'Z' --davidm 1/25/99 */
str = buf;
end = buf + size - 1;
if (end < buf - 1) {
end = ((void *) -1);
size = end - buf + 1;
}
for (; *fmt ; ++fmt) {
if (*fmt != '%') {
if (str <= end)
*str = *fmt;
++str;
continue;
}
/* process flags */
flags = 0;
repeat:
++fmt; /* this also skips first '%' */
switch (*fmt) {
case '-': flags |= LEFT; goto repeat;
case '+': flags |= PLUS; goto repeat;
case ' ': flags |= SPACE; goto repeat;
case '#': flags |= SPECIAL; goto repeat;
case '0': flags |= ZEROPAD; goto repeat;
}
/* get field width */
field_width = -1;
if (isdigit(*fmt))
field_width = skip_atoi(&fmt);
else if (*fmt == '*') {
++fmt;
/* it's the next argument */
field_width = va_arg(args, int);
if (field_width < 0) {
field_width = -field_width;
flags |= LEFT;
}
}
/* get the precision */
precision = -1;
if (*fmt == '.') {
++fmt;
if (isdigit(*fmt))
precision = skip_atoi(&fmt);
else if (*fmt == '*') {
++fmt;
/* it's the next argument */
precision = va_arg(args, int);
}
if (precision < 0)
precision = 0;
}
/* get the conversion qualifier */
qualifier = -1;
if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L' || *fmt =='Z') {
qualifier = *fmt;
++fmt;
if (qualifier == 'l' && *fmt == 'l') {
qualifier = 'L';
++fmt;
}
}
/* default base */
base = 10;
switch (*fmt) {
case 'c':
if (!(flags & LEFT)) {
while (--field_width > 0) {
if (str <= end)
*str = ' ';
++str;
}
}
c = (unsigned char) va_arg(args, int);
if (str <= end)
*str = c;
++str;
while (--field_width > 0) {
if (str <= end)
*str = ' ';
++str;
}
continue;
case 's':
s = va_arg(args, char *);
if (!s)
s = "<NULL>";
len = strnlen(s, precision);
if (!(flags & LEFT)) {
while (len < field_width--) {
if (str <= end)
*str = ' ';
++str;
}
}
for (i = 0; i < len; ++i) {
if (str <= end)
*str = *s;
++str; ++s;
}
while (len < field_width--) {
if (str <= end)
*str = ' ';
++str;
}
continue;
case 'p':
if (field_width == -1) {
field_width = 2*sizeof(void *);
flags |= ZEROPAD;
}
str = number(str, end,
(unsigned long) va_arg(args, void *),
16, field_width, precision, flags);
continue;
case 'n':
/* FIXME:
* What does C99 say about the overflow case here? */
if (qualifier == 'l') {
long * ip = va_arg(args, long *);
*ip = (str - buf);
} else if (qualifier == 'Z') {
size_t * ip = va_arg(args, size_t *);
*ip = (str - buf);
} else {
int * ip = va_arg(args, int *);
*ip = (str - buf);
}
continue;
case '%':
if (str <= end)
*str = '%';
++str;
continue;
/* integer number formats - set up the flags and "break" */
case 'o':
base = 8;
break;
case 'X':
flags |= LARGE;
case 'x':
base = 16;
break;
case 'd':
case 'i':
flags |= SIGN;
case 'u':
break;
default:
if (str <= end)
*str = '%';
++str;
if (*fmt) {
if (str <= end)
*str = *fmt;
++str;
} else {
--fmt;
}
continue;
}
if (qualifier == 'L')
num = va_arg(args, long long);
else if (qualifier == 'l') {
num = va_arg(args, unsigned long);
if (flags & SIGN)
num = (signed long) num;
} else if (qualifier == 'Z') {
num = va_arg(args, size_t);
} else if (qualifier == 'h') {
num = (unsigned short) va_arg(args, int);
if (flags & SIGN)
num = (signed short) num;
} else {
num = va_arg(args, unsigned int);
if (flags & SIGN)
num = (signed int) num;
}
str = number(str, end, num, base,
field_width, precision, flags);
}
if (str <= end)
*str = '\0';
else if (size > 0)
/* don't write out a null byte if the buf size is zero */
*end = '\0';
/* the trailing null byte doesn't count towards the total
* ++str;
*/
return str-buf;
}
/**
* snprintf - Format a string and place it in a buffer
* @buf: The buffer to place the result into
* @size: The size of the buffer, including the trailing null space
* @fmt: The format string to use
* @...: Arguments for the format string
*/
int snprintf(char * buf, size_t size, const char *fmt, ...)
{
va_list args;
int i;
va_start(args, fmt);
i=vsnprintf(buf,size,fmt,args);
va_end(args);
return i;
}
/**
* vsprintf - Format a string and place it in a buffer
* @buf: The buffer to place the result into
* @fmt: The format string to use
* @args: Arguments for the format string
*
* Call this function if you are already dealing with a va_list.
* You probably want sprintf instead.
*/
int vsprintf(char *buf, const char *fmt, va_list args)
{
return vsnprintf(buf, 0xFFFFFFFFUL, fmt, args);
}
/**
* sprintf - Format a string and place it in a buffer
* @buf: The buffer to place the result into
* @fmt: The format string to use
* @...: Arguments for the format string
*/
int sprintf(char * buf, const char *fmt, ...)
{
va_list args;
int i;
va_start(args, fmt);
i=vsprintf(buf,fmt,args);
va_end(args);
return i;
}
/**
* vsscanf - Unformat a buffer into a list of arguments
* @buf: input buffer
* @fmt: format of buffer
* @args: arguments
*/
int vsscanf(const char * buf, const char * fmt, va_list args)
{
const char *str = buf;
char *next;
int num = 0;
int qualifier;
int base;
int field_width = -1;
int is_sign = 0;
while(*fmt && *str) {
/* skip any white space in format */
/* white space in format matchs any amount of
* white space, including none, in the input.
*/
if (isspace(*fmt)) {
while (isspace(*fmt))
++fmt;
while (isspace(*str))
++str;
}
/* anything that is not a conversion must match exactly */
if (*fmt != '%' && *fmt) {
if (*fmt++ != *str++)
break;
continue;
}
if (!*fmt)
break;
++fmt;
/* skip this conversion.
* advance both strings to next white space
*/
if (*fmt == '*') {
while (!isspace(*fmt) && *fmt)
fmt++;
while (!isspace(*str) && *str)
str++;
continue;
}
/* get field width */
if (isdigit(*fmt))
field_width = skip_atoi(&fmt);
/* get conversion qualifier */
qualifier = -1;
if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L' || *fmt == 'Z') {
qualifier = *fmt;
fmt++;
}
base = 10;
is_sign = 0;
if (!*fmt || !*str)
break;
switch(*fmt++) {
case 'c':
{
char *s = (char *) va_arg(args,char*);
if (field_width == -1)
field_width = 1;
do {
*s++ = *str++;
} while(field_width-- > 0 && *str);
num++;
}
continue;
case 's':
{
char *s = (char *) va_arg(args, char *);
if(field_width == -1)
field_width = INT_MAX;
/* first, skip leading white space in buffer */
while (isspace(*str))
str++;
/* now copy until next white space */
while (*str && !isspace(*str) && field_width--) {
*s++ = *str++;
}
*s = '\0';
num++;
}
continue;
case 'n':
/* return number of characters read so far */
{
int *i = (int *)va_arg(args,int*);
*i = str - buf;
}
continue;
case 'o':
base = 8;
break;
case 'x':
case 'X':
base = 16;
break;
case 'd':
case 'i':
is_sign = 1;
case 'u':
break;
case '%':
/* looking for '%' in str */
if (*str++ != '%')
return num;
continue;
default:
/* invalid format; stop here */
return num;
}
/* have some sort of integer conversion.
* first, skip white space in buffer.
*/
while (isspace(*str))
str++;
if (!*str || !isdigit(*str))
break;
switch(qualifier) {
case 'h':
if (is_sign) {
short *s = (short *) va_arg(args,short *);
*s = (short) simple_strtol(str,&next,base);
} else {
unsigned short *s = (unsigned short *) va_arg(args, unsigned short *);
*s = (unsigned short) simple_strtoul(str, &next, base);
}
break;
case 'l':
if (is_sign) {
long *l = (long *) va_arg(args,long *);
*l = simple_strtol(str,&next,base);
} else {
unsigned long *l = (unsigned long*) va_arg(args,unsigned long*);
*l = simple_strtoul(str,&next,base);
}
break;
case 'L':
if (is_sign) {
long long *l = (long long*) va_arg(args,long long *);
*l = simple_strtoll(str,&next,base);
} else {
unsigned long long *l = (unsigned long long*) va_arg(args,unsigned long long*);
*l = simple_strtoull(str,&next,base);
}
break;
case 'Z':
{
size_t *s = (size_t*) va_arg(args,size_t*);
*s = (size_t) simple_strtoul(str,&next,base);
}
break;
default:
if (is_sign) {
int *i = (int *) va_arg(args, int*);
*i = (int) simple_strtol(str,&next,base);
} else {
unsigned int *i = (unsigned int*) va_arg(args, unsigned int*);
*i = (unsigned int) simple_strtoul(str,&next,base);
}
break;
}
num++;
if (!next)
break;
str = next;
}
return num;
}
/**
* sscanf - Unformat a buffer into a list of arguments
* @buf: input buffer
* @fmt: formatting of buffer
* @...: resulting arguments
*/
int sscanf(const char * buf, const char * fmt, ...)
{
va_list args;
int i;
va_start(args,fmt);
i = vsscanf(buf,fmt,args);
va_end(args);
return i;
}
+12
View File
@@ -0,0 +1,12 @@
default:
$(MAKE) -C pci
$(MAKE) -C net
$(MAKE) -C block
$(MAKE) -C ide
clean:
$(MAKE) -C pci clean
$(MAKE) -C net clean
$(MAKE) -C block clean
$(MAKE) -C ide clean
+8
View File
@@ -0,0 +1,8 @@
include $(BASEDIR)/Rules.mk
default: $(OBJS)
$(LD) -r -o driver.o $(OBJS)
clean:
rm -f *.o *~ core
+315
View File
@@ -0,0 +1,315 @@
/*
* Partition table and disk geometry handling
*
* This obsoletes the partition-handling code in genhd.c:
* Userspace can look at a disk in arbitrary format and tell
* the kernel what partitions there are on the disk, and how
* these should be numbered.
* It also allows one to repartition a disk that is being used.
*
* A single ioctl with lots of subfunctions:
*
* Device number stuff:
* get_whole_disk() (given the device number of a partition, find
* the device number of the encompassing disk)
* get_all_partitions() (given the device number of a disk, return the
* device numbers of all its known partitions)
*
* Partition stuff:
* add_partition()
* delete_partition()
* test_partition_in_use() (also for test_disk_in_use)
*
* Geometry stuff:
* get_geometry()
* set_geometry()
* get_bios_drivedata()
*
* For today, only the partition stuff - aeb, 990515
*/
#include <xeno/config.h>
#include <xeno/types.h>
#include <xeno/errno.h>
/*#include <xeno/fs.h> */ /* for BLKRASET, ... */
#include <xeno/sched.h> /* for capable() */
#include <xeno/blk.h> /* for set_device_ro() */
#include <xeno/blkpg.h>
#include <xeno/genhd.h>
/*#include <xeno/swap.h>*/ /* for is_swap_partition() */
#include <xeno/module.h> /* for EXPORT_SYMBOL */
#include <asm/uaccess.h>
#define is_mounted(_dev) (0)
#define is_swap_partition(_dev) (0)
#define fsync_dev(_dev) (panic("fsync_dev???"))
#define invalidate_buffers(_dev) (panic("invalidate_buffers???"))
/*
* What is the data describing a partition?
*
* 1. a device number (kdev_t)
* 2. a starting sector and number of sectors (hd_struct)
* given in the part[] array of the gendisk structure for the drive.
*
* The number of sectors is replicated in the sizes[] array of
* the gendisk structure for the major, which again is copied to
* the blk_size[][] array.
* (However, hd_struct has the number of 512-byte sectors,
* g->sizes[] and blk_size[][] have the number of 1024-byte blocks.)
* Note that several drives may have the same major.
*/
/*
* Add a partition.
*
* returns: EINVAL: bad parameters
* ENXIO: cannot find drive
* EBUSY: proposed partition overlaps an existing one
* or has the same number as an existing one
* 0: all OK.
*/
int add_partition(kdev_t dev, struct blkpg_partition *p) {
struct gendisk *g;
long long ppstart, pplength;
long pstart, plength;
int i, drive, first_minor, end_minor, minor;
/* convert bytes to sectors, check for fit in a hd_struct */
ppstart = (p->start >> 9);
pplength = (p->length >> 9);
pstart = ppstart;
plength = pplength;
if (pstart != ppstart || plength != pplength
|| pstart < 0 || plength < 0)
return -EINVAL;
/* find the drive major */
g = get_gendisk(dev);
if (!g)
return -ENXIO;
/* existing drive? */
drive = (MINOR(dev) >> g->minor_shift);
first_minor = (drive << g->minor_shift);
end_minor = first_minor + g->max_p;
if (drive >= g->nr_real)
return -ENXIO;
/* drive and partition number OK? */
if (first_minor != MINOR(dev) || p->pno <= 0 || p->pno >= g->max_p)
return -EINVAL;
/* partition number in use? */
minor = first_minor + p->pno;
if (g->part[minor].nr_sects != 0)
return -EBUSY;
/* overlap? */
for (i=first_minor+1; i<end_minor; i++)
if (!(pstart+plength <= g->part[i].start_sect ||
pstart >= g->part[i].start_sect + g->part[i].nr_sects))
return -EBUSY;
/* all seems OK */
g->part[minor].start_sect = pstart;
g->part[minor].nr_sects = plength;
if (g->sizes)
g->sizes[minor] = (plength >> (BLOCK_SIZE_BITS - 9));
#ifdef DEVFS_MUST_DIE
devfs_register_partitions (g, first_minor, 0);
#endif
return 0;
}
/*
* Delete a partition given by partition number
*
* returns: EINVAL: bad parameters
* ENXIO: cannot find partition
* EBUSY: partition is busy
* 0: all OK.
*
* Note that the dev argument refers to the entire disk, not the partition.
*/
int del_partition(kdev_t dev, struct blkpg_partition *p) {
struct gendisk *g;
kdev_t devp;
int drive, first_minor, minor;
/* find the drive major */
g = get_gendisk(dev);
if (!g)
return -ENXIO;
/* drive and partition number OK? */
drive = (MINOR(dev) >> g->minor_shift);
first_minor = (drive << g->minor_shift);
if (first_minor != MINOR(dev) || p->pno <= 0 || p->pno >= g->max_p)
return -EINVAL;
/* existing drive and partition? */
minor = first_minor + p->pno;
if (drive >= g->nr_real || g->part[minor].nr_sects == 0)
return -ENXIO;
/* partition in use? Incomplete check for now. */
devp = MKDEV(MAJOR(dev), minor);
if (is_mounted(devp) || is_swap_partition(devp))
return -EBUSY;
/* all seems OK */
fsync_dev(devp);
invalidate_buffers(devp);
g->part[minor].start_sect = 0;
g->part[minor].nr_sects = 0;
if (g->sizes)
g->sizes[minor] = 0;
#ifdef DEVFS_MUST_DIE
devfs_register_partitions (g, first_minor, 0);
#endif
return 0;
}
int blkpg_ioctl(kdev_t dev, struct blkpg_ioctl_arg *arg)
{
struct blkpg_ioctl_arg a;
struct blkpg_partition p;
int len;
if (copy_from_user(&a, arg, sizeof(struct blkpg_ioctl_arg)))
return -EFAULT;
switch (a.op) {
case BLKPG_ADD_PARTITION:
case BLKPG_DEL_PARTITION:
len = a.datalen;
if (len < sizeof(struct blkpg_partition))
return -EINVAL;
if (copy_from_user(&p, a.data, sizeof(struct blkpg_partition)))
return -EFAULT;
if (!capable(CAP_SYS_ADMIN))
return -EACCES;
if (a.op == BLKPG_ADD_PARTITION)
return add_partition(dev, &p);
else
return del_partition(dev, &p);
default:
return -EINVAL;
}
}
/*
* Common ioctl's for block devices
*/
int blk_ioctl(kdev_t dev, unsigned int cmd, unsigned long arg)
{
#if 1
printk("May want to check out blk_ioctl...\n");
return -EINVAL;
#else
struct gendisk *g;
u64 ullval = 0;
int intval;
if (!dev)
return -EINVAL;
switch (cmd) {
case BLKROSET:
if (!capable(CAP_SYS_ADMIN))
return -EACCES;
if (get_user(intval, (int *)(arg)))
return -EFAULT;
set_device_ro(dev, intval);
return 0;
case BLKROGET:
intval = (is_read_only(dev) != 0);
return put_user(intval, (int *)(arg));
case BLKRASET:
if(!capable(CAP_SYS_ADMIN))
return -EACCES;
if(arg > 0xff)
return -EINVAL;
read_ahead[MAJOR(dev)] = arg;
return 0;
case BLKRAGET:
if (!arg)
return -EINVAL;
return put_user(read_ahead[MAJOR(dev)], (long *) arg);
case BLKFLSBUF:
if(!capable(CAP_SYS_ADMIN))
return -EACCES;
fsync_dev(dev);
invalidate_buffers(dev);
return 0;
case BLKSSZGET:
/* get block device sector size as needed e.g. by fdisk */
intval = get_hardsect_size(dev);
return put_user(intval, (int *) arg);
case BLKGETSIZE:
case BLKGETSIZE64:
g = get_gendisk(dev);
if (g)
ullval = g->part[MINOR(dev)].nr_sects;
if (cmd == BLKGETSIZE)
return put_user((unsigned long)ullval, (unsigned long *)arg);
else
return put_user(ullval << 9, (u64 *)arg);
#if 0
case BLKRRPART: /* Re-read partition tables */
if (!capable(CAP_SYS_ADMIN))
return -EACCES;
return reread_partitions(dev, 1);
#endif
case BLKPG:
return blkpg_ioctl(dev, (struct blkpg_ioctl_arg *) arg);
case BLKELVGET:
return blkelvget_ioctl(&blk_get_queue(dev)->elevator,
(blkelv_ioctl_arg_t *) arg);
case BLKELVSET:
return blkelvset_ioctl(&blk_get_queue(dev)->elevator,
(blkelv_ioctl_arg_t *) arg);
case BLKBSZGET:
/* get the logical block size (cf. BLKSSZGET) */
intval = BLOCK_SIZE;
if (blksize_size[MAJOR(dev)])
intval = blksize_size[MAJOR(dev)][MINOR(dev)];
return put_user (intval, (int *) arg);
case BLKBSZSET:
/* set the logical block size */
if (!capable (CAP_SYS_ADMIN))
return -EACCES;
if (!dev || !arg)
return -EINVAL;
if (get_user (intval, (int *) arg))
return -EFAULT;
if (intval > PAGE_SIZE || intval < 512 ||
(intval & (intval - 1)))
return -EINVAL;
if (is_mounted (dev) || is_swap_partition (dev))
return -EBUSY;
set_blocksize (dev, intval);
return 0;
default:
return -EINVAL;
}
#endif
}
EXPORT_SYMBOL(blk_ioctl);
+224
View File
@@ -0,0 +1,224 @@
/*
* linux/drivers/block/elevator.c
*
* Block device elevator/IO-scheduler.
*
* Copyright (C) 2000 Andrea Arcangeli <andrea@suse.de> SuSE
*
* 30042000 Jens Axboe <axboe@suse.de> :
*
* Split the elevator a bit so that it is possible to choose a different
* one or even write a new "plug in". There are three pieces:
* - elevator_fn, inserts a new request in the queue list
* - elevator_merge_fn, decides whether a new buffer can be merged with
* an existing request
* - elevator_dequeue_fn, called when a request is taken off the active list
*
* 20082000 Dave Jones <davej@suse.de> :
* Removed tests for max-bomb-segments, which was breaking elvtune
* when run without -bN
*
*/
#include <xeno/config.h>
#include <xeno/types.h>
/*#include <xeno/fs.h>*/
#include <xeno/blkdev.h>
#include <xeno/elevator.h>
#include <xeno/blk.h>
#include <xeno/module.h>
#include <asm/uaccess.h>
/*
* This is a bit tricky. It's given that bh and rq are for the same
* device, but the next request might of course not be. Run through
* the tests below to check if we want to insert here if we can't merge
* bh into an existing request
*/
inline int bh_rq_in_between(struct buffer_head *bh, struct request *rq,
struct list_head *head)
{
struct list_head *next;
struct request *next_rq;
next = rq->queue.next;
if (next == head)
return 0;
/*
* if the device is different (usually on a different partition),
* just check if bh is after rq
*/
next_rq = blkdev_entry_to_request(next);
if (next_rq->rq_dev != rq->rq_dev)
return bh->b_rsector > rq->sector;
/*
* ok, rq, next_rq and bh are on the same device. if bh is in between
* the two, this is the sweet spot
*/
if (bh->b_rsector < next_rq->sector && bh->b_rsector > rq->sector)
return 1;
/*
* next_rq is ordered wrt rq, but bh is not in between the two
*/
if (next_rq->sector > rq->sector)
return 0;
/*
* next_rq and rq not ordered, if we happen to be either before
* next_rq or after rq insert here anyway
*/
if (bh->b_rsector > rq->sector || bh->b_rsector < next_rq->sector)
return 1;
return 0;
}
int elevator_linus_merge(request_queue_t *q, struct request **req,
struct list_head * head,
struct buffer_head *bh, int rw,
int max_sectors)
{
struct list_head *entry = &q->queue_head;
unsigned int count = bh->b_size >> 9, ret = ELEVATOR_NO_MERGE;
while ((entry = entry->prev) != head) {
struct request *__rq = blkdev_entry_to_request(entry);
/*
* simply "aging" of requests in queue
*/
if (__rq->elevator_sequence-- <= 0)
break;
if (__rq->waiting)
continue;
if (__rq->rq_dev != bh->b_rdev)
continue;
if (!*req && bh_rq_in_between(bh, __rq, &q->queue_head))
*req = __rq;
if (__rq->cmd != rw)
continue;
if (__rq->nr_sectors + count > max_sectors)
continue;
if (__rq->elevator_sequence < count)
break;
if (__rq->sector + __rq->nr_sectors == bh->b_rsector) {
ret = ELEVATOR_BACK_MERGE;
*req = __rq;
break;
} else if (__rq->sector - count == bh->b_rsector) {
ret = ELEVATOR_FRONT_MERGE;
__rq->elevator_sequence -= count;
*req = __rq;
break;
}
}
return ret;
}
void elevator_linus_merge_cleanup(request_queue_t *q, struct request *req, int count)
{
struct list_head *entry = &req->queue, *head = &q->queue_head;
/*
* second pass scan of requests that got passed over, if any
*/
while ((entry = entry->next) != head) {
struct request *tmp = blkdev_entry_to_request(entry);
tmp->elevator_sequence -= count;
}
}
void elevator_linus_merge_req(struct request *req, struct request *next)
{
if (next->elevator_sequence < req->elevator_sequence)
req->elevator_sequence = next->elevator_sequence;
}
/*
* See if we can find a request that this buffer can be coalesced with.
*/
int elevator_noop_merge(request_queue_t *q, struct request **req,
struct list_head * head,
struct buffer_head *bh, int rw,
int max_sectors)
{
struct list_head *entry;
unsigned int count = bh->b_size >> 9;
if (list_empty(&q->queue_head))
return ELEVATOR_NO_MERGE;
entry = &q->queue_head;
while ((entry = entry->prev) != head) {
struct request *__rq = blkdev_entry_to_request(entry);
if (__rq->cmd != rw)
continue;
if (__rq->rq_dev != bh->b_rdev)
continue;
if (__rq->nr_sectors + count > max_sectors)
continue;
if (__rq->waiting)
continue;
if (__rq->sector + __rq->nr_sectors == bh->b_rsector) {
*req = __rq;
return ELEVATOR_BACK_MERGE;
} else if (__rq->sector - count == bh->b_rsector) {
*req = __rq;
return ELEVATOR_FRONT_MERGE;
}
}
*req = blkdev_entry_to_request(q->queue_head.prev);
return ELEVATOR_NO_MERGE;
}
void elevator_noop_merge_cleanup(request_queue_t *q, struct request *req, int count) {}
void elevator_noop_merge_req(struct request *req, struct request *next) {}
int blkelvget_ioctl(elevator_t * elevator, blkelv_ioctl_arg_t * arg)
{
blkelv_ioctl_arg_t output;
output.queue_ID = elevator->queue_ID;
output.read_latency = elevator->read_latency;
output.write_latency = elevator->write_latency;
output.max_bomb_segments = 0;
if (copy_to_user(arg, &output, sizeof(blkelv_ioctl_arg_t)))
return -EFAULT;
return 0;
}
int blkelvset_ioctl(elevator_t * elevator, const blkelv_ioctl_arg_t * arg)
{
blkelv_ioctl_arg_t input;
if (copy_from_user(&input, arg, sizeof(blkelv_ioctl_arg_t)))
return -EFAULT;
if (input.read_latency < 0)
return -EINVAL;
if (input.write_latency < 0)
return -EINVAL;
elevator->read_latency = input.read_latency;
elevator->write_latency = input.write_latency;
return 0;
}
void elevator_init(elevator_t * elevator, elevator_t type)
{
static unsigned int queue_ID;
*elevator = type;
elevator->queue_ID = queue_ID++;
}
+219
View File
@@ -0,0 +1,219 @@
/*
* Code extracted from
* linux/kernel/hd.c
*
* Copyright (C) 1991-1998 Linus Torvalds
*
* devfs support - jj, rgooch, 980122
*
* Moved partition checking code to fs/partitions* - Russell King
* (linux@arm.uk.linux.org)
*/
/*
* TODO: rip out the remaining init crap from this file --hch
*/
#include <xeno/config.h>
#include <xeno/module.h>
/*#include <xeno/fs.h>*/
#include <xeno/genhd.h>
#include <xeno/lib.h>
#include <xeno/blk.h>
#include <xeno/init.h>
#include <xeno/spinlock.h>
static rwlock_t gendisk_lock;
/*
* Global kernel list of partitioning information.
*
* XXX: you should _never_ access this directly.
* the only reason this is exported is source compatiblity.
*/
/*static*/ struct gendisk *gendisk_head;
static struct gendisk *gendisk_array[MAX_BLKDEV];
EXPORT_SYMBOL(gendisk_head);
/**
* add_gendisk - add partitioning information to kernel list
* @gp: per-device partitioning information
*
* This function registers the partitioning information in @gp
* with the kernel.
*/
void
add_gendisk(struct gendisk *gp)
{
struct gendisk *sgp;
write_lock(&gendisk_lock);
/*
* In 2.5 this will go away. Fix the drivers who rely on
* old behaviour.
*/
for (sgp = gendisk_head; sgp; sgp = sgp->next)
{
if (sgp == gp)
{
// printk(KERN_ERR "add_gendisk: device major %d is buggy and added a live gendisk!\n",
// sgp->major)
goto out;
}
}
gendisk_array[gp->major] = gp;
gp->next = gendisk_head;
gendisk_head = gp;
out:
write_unlock(&gendisk_lock);
}
EXPORT_SYMBOL(add_gendisk);
/**
* del_gendisk - remove partitioning information from kernel list
* @gp: per-device partitioning information
*
* This function unregisters the partitioning information in @gp
* with the kernel.
*/
void
del_gendisk(struct gendisk *gp)
{
struct gendisk **gpp;
write_lock(&gendisk_lock);
gendisk_array[gp->major] = NULL;
for (gpp = &gendisk_head; *gpp; gpp = &((*gpp)->next))
if (*gpp == gp)
break;
if (*gpp)
*gpp = (*gpp)->next;
write_unlock(&gendisk_lock);
}
EXPORT_SYMBOL(del_gendisk);
/**
* get_gendisk - get partitioning information for a given device
* @dev: device to get partitioning information for
*
* This function gets the structure containing partitioning
* information for the given device @dev.
*/
struct gendisk *
get_gendisk(kdev_t dev)
{
struct gendisk *gp = NULL;
int maj = MAJOR(dev);
read_lock(&gendisk_lock);
if ((gp = gendisk_array[maj]))
goto out;
/* This is needed for early 2.4 source compatiblity. --hch */
for (gp = gendisk_head; gp; gp = gp->next)
if (gp->major == maj)
break;
out:
read_unlock(&gendisk_lock);
return gp;
}
EXPORT_SYMBOL(get_gendisk);
/**
* walk_gendisk - issue a command for every registered gendisk
* @walk: user-specified callback
* @data: opaque data for the callback
*
* This function walks through the gendisk chain and calls back
* into @walk for every element.
*/
int
walk_gendisk(int (*walk)(struct gendisk *, void *), void *data)
{
struct gendisk *gp;
int error = 0;
read_lock(&gendisk_lock);
for (gp = gendisk_head; gp; gp = gp->next)
if ((error = walk(gp, data)))
break;
read_unlock(&gendisk_lock);
return error;
}
#ifdef CONFIG_PROC_FS
int
get_partition_list(char *page, char **start, off_t offset, int count)
{
struct gendisk *gp;
struct hd_struct *hd;
char buf[64];
int len, n;
len = sprintf(page, "major minor #blocks name\n\n");
read_lock(&gendisk_lock);
for (gp = gendisk_head; gp; gp = gp->next) {
for (n = 0; n < (gp->nr_real << gp->minor_shift); n++) {
if (gp->part[n].nr_sects == 0)
continue;
hd = &gp->part[n]; disk_round_stats(hd);
len += sprintf(page + len,
"%4d %4d %10d %s\n", gp->major,
n, gp->sizes[n], disk_name(gp, n, buf));
if (len < offset)
offset -= len, len = 0;
else if (len >= offset + count)
goto out;
}
}
out:
read_unlock(&gendisk_lock);
*start = page + offset;
len -= offset;
if (len < 0)
len = 0;
return len > count ? count : len;
}
#endif
extern int blk_dev_init(void);
extern int net_dev_init(void);
extern void console_map_init(void);
extern int atmdev_init(void);
int __init device_init(void)
{
rwlock_init(&gendisk_lock);
blk_dev_init();
sti();
#ifdef CONFIG_NET
net_dev_init();
#endif
#ifdef CONFIG_ATM
(void) atmdev_init();
#endif
#ifdef CONFIG_VT
console_map_init();
#endif
return 0;
}
__initcall(device_init);
File diff suppressed because it is too large. Load diff
+8
View File
@@ -0,0 +1,8 @@
include $(BASEDIR)/Rules.mk
default: $(OBJS)
$(LD) -r -o driver.o $(OBJS)
clean:
rm -f *.o *~ core
File diff suppressed because it is too large. Load diff
+867
View File
@@ -0,0 +1,867 @@
/*
* linux/drivers/ide/ide-dma.c Version 4.10 June 9, 2000
*
* Copyright (c) 1999-2000 Andre Hedrick <andre@linux-ide.org>
* May be copied or modified under the terms of the GNU General Public License
*/
/*
* Special Thanks to Mark for his Six years of work.
*
* Copyright (c) 1995-1998 Mark Lord
* May be copied or modified under the terms of the GNU General Public License
*/
/*
* This module provides support for the bus-master IDE DMA functions
* of various PCI chipsets, including the Intel PIIX (i82371FB for
* the 430 FX chipset), the PIIX3 (i82371SB for the 430 HX/VX and
* 440 chipsets), and the PIIX4 (i82371AB for the 430 TX chipset)
* ("PIIX" stands for "PCI ISA IDE Xcellerator").
*
* Pretty much the same code works for other IDE PCI bus-mastering chipsets.
*
* DMA is supported for all IDE devices (disk drives, cdroms, tapes, floppies).
*
* By default, DMA support is prepared for use, but is currently enabled only
* for drives which already have DMA enabled (UltraDMA or mode 2 multi/single),
* or which are recognized as "good" (see table below). Drives with only mode0
* or mode1 (multi/single) DMA should also work with this chipset/driver
* (eg. MC2112A) but are not enabled by default.
*
* Use "hdparm -i" to view modes supported by a given drive.
*
* The hdparm-3.5 (or later) utility can be used for manually enabling/disabling
* DMA support, but must be (re-)compiled against this kernel version or later.
*
* To enable DMA, use "hdparm -d1 /dev/hd?" on a per-drive basis after booting.
* If problems arise, ide.c will disable DMA operation after a few retries.
* This error recovery mechanism works and has been extremely well exercised.
*
* IDE drives, depending on their vintage, may support several different modes
* of DMA operation. The boot-time modes are indicated with a "*" in
* the "hdparm -i" listing, and can be changed with *knowledgeable* use of
* the "hdparm -X" feature. There is seldom a need to do this, as drives
* normally power-up with their "best" PIO/DMA modes enabled.
*
* Testing has been done with a rather extensive number of drives,
* with Quantum & Western Digital models generally outperforming the pack,
* and Fujitsu & Conner (and some Seagate which are really Conner) drives
* showing more lackluster throughput.
*
* Keep an eye on /var/adm/messages for "DMA disabled" messages.
*
* Some people have reported trouble with Intel Zappa motherboards.
* This can be fixed by upgrading the AMI BIOS to version 1.00.04.BS0,
* available from ftp://ftp.intel.com/pub/bios/10004bs0.exe
* (thanks to Glen Morrell <glen@spin.Stanford.edu> for researching this).
*
* Thanks to "Christopher J. Reimer" <reimer@doe.carleton.ca> for
* fixing the problem with the BIOS on some Acer motherboards.
*
* Thanks to "Benoit Poulot-Cazajous" <poulot@chorus.fr> for testing
* "TX" chipset compatibility and for providing patches for the "TX" chipset.
*
* Thanks to Christian Brunner <chb@muc.de> for taking a good first crack
* at generic DMA -- his patches were referred to when preparing this code.
*
* Most importantly, thanks to Robert Bringman <rob@mars.trion.com>
* for supplying a Promise UDMA board & WD UDMA drive for this work!
*
* And, yes, Intel Zappa boards really *do* use both PIIX IDE ports.
*
* check_drive_lists(ide_drive_t *drive, int good_bad)
*
* ATA-66/100 and recovery functions, I forgot the rest......
* SELECT_READ_WRITE(hwif,drive,func) for active tuning based on IO direction.
*
*/
#include <xeno/config.h>
#include <xeno/types.h>
#include <xeno/lib.h>
#include <xeno/timer.h>
#include <xeno/mm.h>
#include <xeno/interrupt.h>
#include <xeno/pci.h>
#include <xeno/init.h>
#include <xeno/ide.h>
#include <asm/byteorder.h>
#include <asm/io.h>
#include <asm/irq.h>
/*
* Long lost data from 2.0.34 that is now in 2.0.39
*
* This was used in ./drivers/block/triton.c to do DMA Base address setup
* when PnP failed. Oh the things we forget. I believe this was part
* of SFF-8038i that has been withdrawn from public access... :-((
*/
#define DEFAULT_BMIBA 0xe800 /* in case BIOS did not init it */
#define DEFAULT_BMCRBA 0xcc00 /* VIA's default value */
#define DEFAULT_BMALIBA 0xd400 /* ALI's default value */
extern char *ide_dmafunc_verbose(ide_dma_action_t dmafunc);
#ifdef CONFIG_IDEDMA_NEW_DRIVE_LISTINGS
struct drive_list_entry {
char * id_model;
char * id_firmware;
};
struct drive_list_entry drive_whitelist [] = {
{ "Micropolis 2112A" , "ALL" },
{ "CONNER CTMA 4000" , "ALL" },
{ "CONNER CTT8000-A" , "ALL" },
{ "ST34342A" , "ALL" },
{ 0 , 0 }
};
struct drive_list_entry drive_blacklist [] = {
{ "WDC AC11000H" , "ALL" },
{ "WDC AC22100H" , "ALL" },
{ "WDC AC31000H" , "ALL" },
{ "WDC AC32500H" , "ALL" },
{ "WDC AC33100H" , "ALL" },
{ "WDC AC31600H" , "ALL" },
{ "WDC AC32100H" , "24.09P07" },
{ "WDC AC23200L" , "21.10N21" },
{ "Compaq CRD-8241B" , "ALL" },
{ "CRD-8400B" , "ALL" },
{ "CRD-8480B", "ALL" },
{ "CRD-8480C", "ALL" },
{ "CRD-8482B", "ALL" },
{ "CRD-84" , "ALL" },
{ "SanDisk SDP3B" , "ALL" },
{ "SanDisk SDP3B-64" , "ALL" },
{ "SANYO CD-ROM CRD" , "ALL" },
{ "HITACHI CDR-8" , "ALL" },
{ "HITACHI CDR-8335" , "ALL" },
{ "HITACHI CDR-8435" , "ALL" },
{ "Toshiba CD-ROM XM-6202B" , "ALL" },
{ "CD-532E-A" , "ALL" },
{ "E-IDE CD-ROM CR-840", "ALL" },
{ "CD-ROM Drive/F5A", "ALL" },
{ "RICOH CD-R/RW MP7083A", "ALL" },
{ "WPI CDD-820", "ALL" },
{ "SAMSUNG CD-ROM SC-148C", "ALL" },
{ "SAMSUNG CD-ROM SC-148F", "ALL" },
{ "SAMSUNG CD-ROM SC", "ALL" },
{ "SanDisk SDP3B-64" , "ALL" },
{ "SAMSUNG CD-ROM SN-124", "ALL" },
{ "PLEXTOR CD-R PX-W8432T", "ALL" },
{ "ATAPI CD-ROM DRIVE 40X MAXIMUM", "ALL" },
{ "_NEC DV5800A", "ALL" },
{ 0 , 0 }
};
int in_drive_list(struct hd_driveid *id, struct drive_list_entry * drive_table)
{
for ( ; drive_table->id_model ; drive_table++)
if ((!strcmp(drive_table->id_model, id->model)) &&
((!strstr(drive_table->id_firmware, id->fw_rev)) ||
(!strcmp(drive_table->id_firmware, "ALL"))))
return 1;
return 0;
}
#else /* !CONFIG_IDEDMA_NEW_DRIVE_LISTINGS */
/*
* good_dma_drives() lists the model names (from "hdparm -i")
* of drives which do not support mode2 DMA but which are
* known to work fine with this interface under Linux.
*/
const char *good_dma_drives[] = {"Micropolis 2112A",
"CONNER CTMA 4000",
"CONNER CTT8000-A",
"ST34342A", /* for Sun Ultra */
NULL};
/*
* bad_dma_drives() lists the model names (from "hdparm -i")
* of drives which supposedly support (U)DMA but which are
* known to corrupt data with this interface under Linux.
*
* This is an empirical list. Its generated from bug reports. That means
* while it reflects actual problem distributions it doesn't answer whether
* the drive or the controller, or cabling, or software, or some combination
* thereof is the fault. If you don't happen to agree with the kernel's
* opinion of your drive - use hdparm to turn DMA on.
*/
const char *bad_dma_drives[] = {"WDC AC11000H",
"WDC AC22100H",
"WDC AC32100H",
"WDC AC32500H",
"WDC AC33100H",
"WDC AC31600H",
NULL};
#endif /* CONFIG_IDEDMA_NEW_DRIVE_LISTINGS */
/*
* Our Physical Region Descriptor (PRD) table should be large enough
* to handle the biggest I/O request we are likely to see. Since requests
* can have no more than 256 sectors, and since the typical blocksize is
* two or more sectors, we could get by with a limit of 128 entries here for
* the usual worst case. Most requests seem to include some contiguous blocks,
* further reducing the number of table entries required.
*
* The driver reverts to PIO mode for individual requests that exceed
* this limit (possible with 512 byte blocksizes, eg. MSDOS f/s), so handling
* 100% of all crazy scenarios here is not necessary.
*
* As it turns out though, we must allocate a full 4KB page for this,
* so the two PRD tables (ide0 & ide1) will each get half of that,
* allowing each to have about 256 entries (8 bytes each) from this.
*/
#define PRD_BYTES 8
#define PRD_ENTRIES (PAGE_SIZE / (2 * PRD_BYTES))
/*
* dma_intr() is the handler for disk read/write DMA interrupts
*/
ide_startstop_t ide_dma_intr (ide_drive_t *drive)
{
int i;
byte stat, dma_stat;
dma_stat = HWIF(drive)->dmaproc(ide_dma_end, drive);
stat = GET_STAT(); /* get drive status */
if (OK_STAT(stat,DRIVE_READY,drive->bad_wstat|DRQ_STAT)) {
if (!dma_stat) {
struct request *rq = HWGROUP(drive)->rq;
rq = HWGROUP(drive)->rq;
for (i = rq->nr_sectors; i > 0;) {
i -= rq->current_nr_sectors;
ide_end_request(1, HWGROUP(drive));
}
return ide_stopped;
}
printk("%s: dma_intr: bad DMA status (dma_stat=%x)\n",
drive->name, dma_stat);
}
return ide_error(drive, "dma_intr", stat);
}
static int ide_build_sglist (ide_hwif_t *hwif, struct request *rq)
{
struct buffer_head *bh;
struct scatterlist *sg = hwif->sg_table;
int nents = 0;
if (hwif->sg_dma_active)
BUG();
if (rq->cmd == READ)
hwif->sg_dma_direction = PCI_DMA_FROMDEVICE;
else
hwif->sg_dma_direction = PCI_DMA_TODEVICE;
bh = rq->bh;
do {
unsigned char *virt_addr = bh->b_data;
unsigned int size = bh->b_size;
if (nents >= PRD_ENTRIES)
return 0;
while ((bh = bh->b_reqnext) != NULL) {
if ((virt_addr + size) != (unsigned char *) bh->b_data)
break;
size += bh->b_size;
}
memset(&sg[nents], 0, sizeof(*sg));
sg[nents].address = virt_addr;
sg[nents].length = size;
nents++;
} while (bh != NULL);
return pci_map_sg(hwif->pci_dev, sg, nents, hwif->sg_dma_direction);
}
static int ide_raw_build_sglist (ide_hwif_t *hwif, struct request *rq)
{
struct scatterlist *sg = hwif->sg_table;
int nents = 0;
ide_task_t *args = rq->special;
unsigned char *virt_addr = rq->buffer;
int sector_count = rq->nr_sectors;
// if ((args->tfRegister[IDE_COMMAND_OFFSET] == WIN_WRITEDMA) ||
// (args->tfRegister[IDE_COMMAND_OFFSET] == WIN_WRITEDMA_EXT))
if (args->command_type == IDE_DRIVE_TASK_RAW_WRITE)
hwif->sg_dma_direction = PCI_DMA_TODEVICE;
else
hwif->sg_dma_direction = PCI_DMA_FROMDEVICE;
if (sector_count > 128) {
memset(&sg[nents], 0, sizeof(*sg));
sg[nents].address = virt_addr;
sg[nents].length = 128 * SECTOR_SIZE;
nents++;
virt_addr = virt_addr + (128 * SECTOR_SIZE);
sector_count -= 128;
}
memset(&sg[nents], 0, sizeof(*sg));
sg[nents].address = virt_addr;
sg[nents].length = sector_count * SECTOR_SIZE;
nents++;
return pci_map_sg(hwif->pci_dev, sg, nents, hwif->sg_dma_direction);
}
/*
* ide_build_dmatable() prepares a dma request.
* Returns 0 if all went okay, returns 1 otherwise.
* May also be invoked from trm290.c
*/
int ide_build_dmatable (ide_drive_t *drive, ide_dma_action_t func)
{
unsigned int *table = HWIF(drive)->dmatable_cpu;
#ifdef CONFIG_BLK_DEV_TRM290
unsigned int is_trm290_chipset = (HWIF(drive)->chipset == ide_trm290);
#else
const int is_trm290_chipset = 0;
#endif
unsigned int count = 0;
int i;
struct scatterlist *sg;
if (HWGROUP(drive)->rq->cmd == IDE_DRIVE_TASKFILE)
HWIF(drive)->sg_nents = i = ide_raw_build_sglist(HWIF(drive), HWGROUP(drive)->rq);
else
HWIF(drive)->sg_nents = i = ide_build_sglist(HWIF(drive), HWGROUP(drive)->rq);
if (!i)
return 0;
sg = HWIF(drive)->sg_table;
while (i && sg_dma_len(sg)) {
u32 cur_addr;
u32 cur_len;
cur_addr = sg_dma_address(sg);
cur_len = sg_dma_len(sg);
/*
* Fill in the dma table, without crossing any 64kB boundaries.
* Most hardware requires 16-bit alignment of all blocks,
* but the trm290 requires 32-bit alignment.
*/
while (cur_len) {
if (count++ >= PRD_ENTRIES) {
printk("%s: DMA table too small\n", drive->name);
goto use_pio_instead;
} else {
u32 xcount, bcount = 0x10000 - (cur_addr & 0xffff);
if (bcount > cur_len)
bcount = cur_len;
*table++ = cpu_to_le32(cur_addr);
xcount = bcount & 0xffff;
if (is_trm290_chipset)
xcount = ((xcount >> 2) - 1) << 16;
if (xcount == 0x0000) {
/*
* Most chipsets correctly interpret a length of 0x0000 as 64KB,
* but at least one (e.g. CS5530) misinterprets it as zero (!).
* So here we break the 64KB entry into two 32KB entries instead.
*/
if (count++ >= PRD_ENTRIES) {
printk("%s: DMA table too small\n", drive->name);
goto use_pio_instead;
}
*table++ = cpu_to_le32(0x8000);
*table++ = cpu_to_le32(cur_addr + 0x8000);
xcount = 0x8000;
}
*table++ = cpu_to_le32(xcount);
cur_addr += bcount;
cur_len -= bcount;
}
}
sg++;
i--;
}
if (count) {
if (!is_trm290_chipset)
*--table |= cpu_to_le32(0x80000000);
return count;
}
printk("%s: empty DMA table?\n", drive->name);
use_pio_instead:
pci_unmap_sg(HWIF(drive)->pci_dev,
HWIF(drive)->sg_table,
HWIF(drive)->sg_nents,
HWIF(drive)->sg_dma_direction);
HWIF(drive)->sg_dma_active = 0;
return 0; /* revert to PIO for this request */
}
/* Teardown mappings after DMA has completed. */
void ide_destroy_dmatable (ide_drive_t *drive)
{
struct pci_dev *dev = HWIF(drive)->pci_dev;
struct scatterlist *sg = HWIF(drive)->sg_table;
int nents = HWIF(drive)->sg_nents;
pci_unmap_sg(dev, sg, nents, HWIF(drive)->sg_dma_direction);
HWIF(drive)->sg_dma_active = 0;
}
/*
* For both Blacklisted and Whitelisted drives.
* This is setup to be called as an extern for future support
* to other special driver code.
*/
int check_drive_lists (ide_drive_t *drive, int good_bad)
{
struct hd_driveid *id = drive->id;
#ifdef CONFIG_IDEDMA_NEW_DRIVE_LISTINGS
if (good_bad) {
return in_drive_list(id, drive_whitelist);
} else {
int blacklist = in_drive_list(id, drive_blacklist);
if (blacklist)
printk("%s: Disabling (U)DMA for %s\n", drive->name, id->model);
return(blacklist);
}
#else /* !CONFIG_IDEDMA_NEW_DRIVE_LISTINGS */
const char **list;
if (good_bad) {
/* Consult the list of known "good" drives */
list = good_dma_drives;
while (*list) {
if (!strcmp(*list++,id->model))
return 1;
}
} else {
/* Consult the list of known "bad" drives */
list = bad_dma_drives;
while (*list) {
if (!strcmp(*list++,id->model)) {
printk("%s: Disabling (U)DMA for %s\n",
drive->name, id->model);
return 1;
}
}
}
#endif /* CONFIG_IDEDMA_NEW_DRIVE_LISTINGS */
return 0;
}
int report_drive_dmaing (ide_drive_t *drive)
{
struct hd_driveid *id = drive->id;
if ((id->field_valid & 4) && (eighty_ninty_three(drive)) &&
(id->dma_ultra & (id->dma_ultra >> 14) & 3)) {
if ((id->dma_ultra >> 15) & 1) {
printk(", UDMA(mode 7)"); /* UDMA BIOS-enabled! */
} else {
printk(", UDMA(133)"); /* UDMA BIOS-enabled! */
}
} else if ((id->field_valid & 4) && (eighty_ninty_three(drive)) &&
(id->dma_ultra & (id->dma_ultra >> 11) & 7)) {
if ((id->dma_ultra >> 13) & 1) {
printk(", UDMA(100)"); /* UDMA BIOS-enabled! */
} else if ((id->dma_ultra >> 12) & 1) {
printk(", UDMA(66)"); /* UDMA BIOS-enabled! */
} else {
printk(", UDMA(44)"); /* UDMA BIOS-enabled! */
}
} else if ((id->field_valid & 4) &&
(id->dma_ultra & (id->dma_ultra >> 8) & 7)) {
if ((id->dma_ultra >> 10) & 1) {
printk(", UDMA(33)"); /* UDMA BIOS-enabled! */
} else if ((id->dma_ultra >> 9) & 1) {
printk(", UDMA(25)"); /* UDMA BIOS-enabled! */
} else {
printk(", UDMA(16)"); /* UDMA BIOS-enabled! */
}
} else if (id->field_valid & 4) {
printk(", (U)DMA"); /* Can be BIOS-enabled! */
} else {
printk(", DMA");
}
return 1;
}
static int config_drive_for_dma (ide_drive_t *drive)
{
int config_allows_dma = 1;
struct hd_driveid *id = drive->id;
ide_hwif_t *hwif = HWIF(drive);
#ifdef CONFIG_IDEDMA_ONLYDISK
if (drive->media != ide_disk)
config_allows_dma = 0;
#endif
if (id && (id->capability & 1) && hwif->autodma && config_allows_dma) {
/* Consult the list of known "bad" drives */
if (ide_dmaproc(ide_dma_bad_drive, drive))
return hwif->dmaproc(ide_dma_off, drive);
/* Enable DMA on any drive that has UltraDMA (mode 6/7/?) enabled */
if ((id->field_valid & 4) && (eighty_ninty_three(drive)))
if ((id->dma_ultra & (id->dma_ultra >> 14) & 2))
return hwif->dmaproc(ide_dma_on, drive);
/* Enable DMA on any drive that has UltraDMA (mode 3/4/5) enabled */
if ((id->field_valid & 4) && (eighty_ninty_three(drive)))
if ((id->dma_ultra & (id->dma_ultra >> 11) & 7))
return hwif->dmaproc(ide_dma_on, drive);
/* Enable DMA on any drive that has UltraDMA (mode 0/1/2) enabled */
if (id->field_valid & 4) /* UltraDMA */
if ((id->dma_ultra & (id->dma_ultra >> 8) & 7))
return hwif->dmaproc(ide_dma_on, drive);
/* Enable DMA on any drive that has mode2 DMA (multi or single) enabled */
if (id->field_valid & 2) /* regular DMA */
if ((id->dma_mword & 0x404) == 0x404 || (id->dma_1word & 0x404) == 0x404)
return hwif->dmaproc(ide_dma_on, drive);
/* Consult the list of known "good" drives */
if (ide_dmaproc(ide_dma_good_drive, drive))
return hwif->dmaproc(ide_dma_on, drive);
}
return hwif->dmaproc(ide_dma_off_quietly, drive);
}
#ifndef CONFIG_BLK_DEV_IDEDMA_TIMEOUT
/*
* 1 dmaing, 2 error, 4 intr
*/
static int dma_timer_expiry (ide_drive_t *drive)
{
byte dma_stat = inb(HWIF(drive)->dma_base+2);
#ifdef DEBUG
printk("%s: dma_timer_expiry: dma status == 0x%02x\n", drive->name, dma_stat);
#endif /* DEBUG */
#if 0
HWGROUP(drive)->expiry = NULL; /* one free ride for now */
#endif
if (dma_stat & 2) { /* ERROR */
byte stat = GET_STAT();
return ide_error(drive, "dma_timer_expiry", stat);
}
if (dma_stat & 1) /* DMAing */
return WAIT_CMD;
return 0;
}
#else /* CONFIG_BLK_DEV_IDEDMA_TIMEOUT */
static ide_startstop_t ide_dma_timeout_revovery (ide_drive_t *drive)
{
ide_hwgroup_t *hwgroup = HWGROUP(drive);
ide_hwif_t *hwif = HWIF(drive);
int enable_dma = drive->using_dma;
unsigned long flags;
ide_startstop_t startstop;
spin_lock_irqsave(&io_request_lock, flags);
hwgroup->handler = NULL;
del_timer(&hwgroup->timer);
spin_unlock_irqrestore(&io_request_lock, flags);
drive->waiting_for_dma = 0;
startstop = ide_do_reset(drive);
if ((enable_dma) && !(drive->using_dma))
(void) hwif->dmaproc(ide_dma_on, drive);
return startstop;
}
#endif /* CONFIG_BLK_DEV_IDEDMA_TIMEOUT */
/*
* ide_dmaproc() initiates/aborts DMA read/write operations on a drive.
*
* The caller is assumed to have selected the drive and programmed the drive's
* sector address using CHS or LBA. All that remains is to prepare for DMA
* and then issue the actual read/write DMA/PIO command to the drive.
*
* For ATAPI devices, we just prepare for DMA and return. The caller should
* then issue the packet command to the drive and call us again with
* ide_dma_begin afterwards.
*
* Returns 0 if all went well.
* Returns 1 if DMA read/write could not be started, in which case
* the caller should revert to PIO for the current request.
* May also be invoked from trm290.c
*/
int ide_dmaproc (ide_dma_action_t func, ide_drive_t *drive)
{
// ide_hwgroup_t *hwgroup = HWGROUP(drive);
ide_hwif_t *hwif = HWIF(drive);
unsigned long dma_base = hwif->dma_base;
byte unit = (drive->select.b.unit & 0x01);
unsigned int count, reading = 0;
byte dma_stat;
switch (func) {
case ide_dma_off:
printk("%s: DMA disabled\n", drive->name);
case ide_dma_off_quietly:
outb(inb(dma_base+2) & ~(1<<(5+unit)), dma_base+2);
case ide_dma_on:
drive->using_dma = (func == ide_dma_on);
if (drive->using_dma)
outb(inb(dma_base+2)|(1<<(5+unit)), dma_base+2);
return 0;
case ide_dma_check:
return config_drive_for_dma (drive);
case ide_dma_read:
reading = 1 << 3;
case ide_dma_write:
SELECT_READ_WRITE(hwif,drive,func);
if (!(count = ide_build_dmatable(drive, func)))
return 1; /* try PIO instead of DMA */
outl(hwif->dmatable_dma, dma_base + 4); /* PRD table */
outb(reading, dma_base); /* specify r/w */
outb(inb(dma_base+2)|6, dma_base+2); /* clear INTR & ERROR flags */
drive->waiting_for_dma = 1;
if (drive->media != ide_disk)
return 0;
#ifdef CONFIG_BLK_DEV_IDEDMA_TIMEOUT
ide_set_handler(drive, &ide_dma_intr, 2*WAIT_CMD, NULL); /* issue cmd to drive */
#else /* !CONFIG_BLK_DEV_IDEDMA_TIMEOUT */
ide_set_handler(drive, &ide_dma_intr, WAIT_CMD, dma_timer_expiry); /* issue cmd to drive */
#endif /* CONFIG_BLK_DEV_IDEDMA_TIMEOUT */
if ((HWGROUP(drive)->rq->cmd == IDE_DRIVE_TASKFILE) &&
(drive->addressing == 1)) {
ide_task_t *args = HWGROUP(drive)->rq->special;
OUT_BYTE(args->tfRegister[IDE_COMMAND_OFFSET], IDE_COMMAND_REG);
} else if (drive->addressing) {
OUT_BYTE(reading ? WIN_READDMA_EXT : WIN_WRITEDMA_EXT, IDE_COMMAND_REG);
} else {
OUT_BYTE(reading ? WIN_READDMA : WIN_WRITEDMA, IDE_COMMAND_REG);
}
return HWIF(drive)->dmaproc(ide_dma_begin, drive);
case ide_dma_begin:
/* Note that this is done *after* the cmd has
* been issued to the drive, as per the BM-IDE spec.
* The Promise Ultra33 doesn't work correctly when
* we do this part before issuing the drive cmd.
*/
outb(inb(dma_base)|1, dma_base); /* start DMA */
return 0;
case ide_dma_end: /* returns 1 on error, 0 otherwise */
drive->waiting_for_dma = 0;
outb(inb(dma_base)&~1, dma_base); /* stop DMA */
dma_stat = inb(dma_base+2); /* get DMA status */
outb(dma_stat|6, dma_base+2); /* clear the INTR & ERROR bits */
ide_destroy_dmatable(drive); /* purge DMA mappings */
return (dma_stat & 7) != 4 ? (0x10 | dma_stat) : 0; /* verify good DMA status */
case ide_dma_test_irq: /* returns 1 if dma irq issued, 0 otherwise */
dma_stat = inb(dma_base+2);
#if 0 /* do not set unless you know what you are doing */
if (dma_stat & 4) {
byte stat = GET_STAT();
outb(dma_base+2, dma_stat & 0xE4);
}
#endif
return (dma_stat & 4) == 4; /* return 1 if INTR asserted */
case ide_dma_bad_drive:
case ide_dma_good_drive:
return check_drive_lists(drive, (func == ide_dma_good_drive));
case ide_dma_verbose:
return report_drive_dmaing(drive);
case ide_dma_timeout:
// FIXME: Many IDE chipsets do not permit command file register access
// FIXME: while the bus-master function is still active.
// FIXME: To prevent deadlock with those chipsets, we must be extremely
// FIXME: careful here (and in ide_intr() as well) to NOT access any
// FIXME: registers from the 0x1Fx/0x17x sets before terminating the
// FIXME: bus-master operation via the bus-master control reg.
// FIXME: Otherwise, chipset deadlock will occur, and some systems will
// FIXME: lock up completely!!
#ifdef CONFIG_BLK_DEV_IDEDMA_TIMEOUT
/*
* Have to issue an abort and requeue the request
* DMA engine got turned off by a goofy ASIC, and
* we have to clean up the mess, and here is as good
* as any. Do it globally for all chipsets.
*/
outb(0x00, dma_base); /* stop DMA */
dma_stat = inb(dma_base+2); /* get DMA status */
outb(dma_stat|6, dma_base+2); /* clear the INTR & ERROR bits */
printk("%s: %s: Lets do it again!" \
"stat = 0x%02x, dma_stat = 0x%02x\n",
drive->name, ide_dmafunc_verbose(func),
GET_STAT(), dma_stat);
if (dma_stat & 0xF0)
return ide_dma_timeout_revovery(drive);
printk("%s: %s: (restart_request) Lets do it again!" \
"stat = 0x%02x, dma_stat = 0x%02x\n",
drive->name, ide_dmafunc_verbose(func),
GET_STAT(), dma_stat);
return restart_request(drive); // BUG: return types do not match!!
//#else
// return HWGROUP(drive)->handler(drive);
#endif /* CONFIG_BLK_DEV_IDEDMA_TIMEOUT */
case ide_dma_retune:
case ide_dma_lostirq:
printk("ide_dmaproc: chipset supported %s func only: %d\n", ide_dmafunc_verbose(func), func);
return 1;
default:
printk("ide_dmaproc: unsupported %s func: %d\n", ide_dmafunc_verbose(func), func);
return 1;
}
}
/*
* Needed for allowing full modular support of ide-driver
*/
int ide_release_dma (ide_hwif_t *hwif)
{
if (hwif->dmatable_cpu) {
pci_free_consistent(hwif->pci_dev,
PRD_ENTRIES * PRD_BYTES,
hwif->dmatable_cpu,
hwif->dmatable_dma);
hwif->dmatable_cpu = NULL;
}
if (hwif->sg_table) {
kfree(hwif->sg_table);
hwif->sg_table = NULL;
}
if ((hwif->dma_extra) && (hwif->channel == 0))
release_region((hwif->dma_base + 16), hwif->dma_extra);
release_region(hwif->dma_base, 8);
return 1;
}
/*
* This can be called for a dynamically installed interface. Don't __init it
*/
void ide_setup_dma (ide_hwif_t *hwif, unsigned long dma_base, unsigned int num_ports)
{
printk(" %s: BM-DMA at 0x%04lx-0x%04lx", hwif->name, dma_base, dma_base + num_ports - 1);
if (check_region(dma_base, num_ports)) {
printk(" -- ERROR, PORT ADDRESSES ALREADY IN USE\n");
return;
}
request_region(dma_base, num_ports, hwif->name);
hwif->dma_base = dma_base;
hwif->dmatable_cpu = pci_alloc_consistent(hwif->pci_dev,
PRD_ENTRIES * PRD_BYTES,
&hwif->dmatable_dma);
if (hwif->dmatable_cpu == NULL)
goto dma_alloc_failure;
hwif->sg_table = kmalloc(sizeof(struct scatterlist) * PRD_ENTRIES,
GFP_KERNEL);
if (hwif->sg_table == NULL) {
pci_free_consistent(hwif->pci_dev, PRD_ENTRIES * PRD_BYTES,
hwif->dmatable_cpu, hwif->dmatable_dma);
goto dma_alloc_failure;
}
hwif->dmaproc = &ide_dmaproc;
if (hwif->chipset != ide_trm290) {
byte dma_stat = inb(dma_base+2);
printk(", BIOS settings: %s:%s, %s:%s",
hwif->drives[0].name, (dma_stat & 0x20) ? "DMA" : "pio",
hwif->drives[1].name, (dma_stat & 0x40) ? "DMA" : "pio");
}
printk("\n");
return;
dma_alloc_failure:
printk(" -- ERROR, UNABLE TO ALLOCATE DMA TABLES\n");
}
/*
* Fetch the DMA Bus-Master-I/O-Base-Address (BMIBA) from PCI space:
*/
unsigned long __init ide_get_or_set_dma_base (ide_hwif_t *hwif, int extra, const char *name)
{
unsigned long dma_base = 0;
struct pci_dev *dev = hwif->pci_dev;
#ifdef CONFIG_BLK_DEV_IDEDMA_FORCED
int second_chance = 0;
second_chance_to_dma:
#endif /* CONFIG_BLK_DEV_IDEDMA_FORCED */
if (hwif->mate && hwif->mate->dma_base) {
dma_base = hwif->mate->dma_base - (hwif->channel ? 0 : 8);
} else {
dma_base = pci_resource_start(dev, 4);
if (!dma_base) {
printk("%s: dma_base is invalid (0x%04lx)\n", name, dma_base);
dma_base = 0;
}
}
#ifdef CONFIG_BLK_DEV_IDEDMA_FORCED
if ((!dma_base) && (!second_chance)) {
unsigned long set_bmiba = 0;
second_chance++;
switch(dev->vendor) {
case PCI_VENDOR_ID_AL:
set_bmiba = DEFAULT_BMALIBA; break;
case PCI_VENDOR_ID_VIA:
set_bmiba = DEFAULT_BMCRBA; break;
case PCI_VENDOR_ID_INTEL:
set_bmiba = DEFAULT_BMIBA; break;
default:
return dma_base;
}
pci_write_config_dword(dev, 0x20, set_bmiba|1);
goto second_chance_to_dma;
}
#endif /* CONFIG_BLK_DEV_IDEDMA_FORCED */
if (dma_base) {
if (extra) /* PDC20246, PDC20262, HPT343, & HPT366 */
request_region(dma_base+16, extra, name);
dma_base += hwif->channel ? 8 : 0;
hwif->dma_extra = extra;
switch(dev->device) {
case PCI_DEVICE_ID_AL_M5219:
case PCI_DEVICE_ID_AMD_VIPER_7409:
case PCI_DEVICE_ID_CMD_643:
outb(inb(dma_base+2) & 0x60, dma_base+2);
if (inb(dma_base+2) & 0x80) {
printk("%s: simplex device: DMA forced\n", name);
}
break;
default:
/*
* If the device claims "simplex" DMA,
* this means only one of the two interfaces
* can be trusted with DMA at any point in time.
* So we should enable DMA only on one of the
* two interfaces.
*/
if ((inb(dma_base+2) & 0x80)) { /* simplex device? */
if ((!hwif->drives[0].present && !hwif->drives[1].present) ||
(hwif->mate && hwif->mate->dma_base)) {
printk("%s: simplex device: DMA disabled\n", name);
dma_base = 0;
}
}
}
}
return dma_base;
}
+384
View File
@@ -0,0 +1,384 @@
/*
* linux/drivers/block/ide-features.c Version 0.04 June 9, 2000
*
* Copyright (C) 1999-2000 Linus Torvalds & authors (see below)
*
* Copyright (C) 1999-2000 Andre Hedrick <andre@linux-ide.org>
*
* Extracts if ide.c to address the evolving transfer rate code for
* the SETFEATURES_XFER callouts. Various parts of any given function
* are credited to previous ATA-IDE maintainers.
*
* Auto-CRC downgrade for Ultra DMA(ing)
*
* May be copied or modified under the terms of the GNU General Public License
*/
#include <xeno/config.h>
#define __NO_VERSION__
#include <xeno/module.h>
#include <xeno/types.h>
#include <xeno/lib.h>
#include <xeno/timer.h>
#include <xeno/mm.h>
#include <xeno/interrupt.h>
#include <xeno/major.h>
#include <xeno/errno.h>
#include <xeno/genhd.h>
#include <xeno/blkpg.h>
#include <xeno/slab.h>
#include <xeno/pci.h>
#include <xeno/delay.h>
#include <xeno/hdreg.h>
#include <xeno/ide.h>
#include <asm/byteorder.h>
#include <asm/irq.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <asm/bitops.h>
/*
* A Verbose noise maker for debugging on the attempted transfer rates.
*/
char *ide_xfer_verbose (byte xfer_rate)
{
switch(xfer_rate) {
case XFER_UDMA_7: return("UDMA 7");
case XFER_UDMA_6: return("UDMA 6");
case XFER_UDMA_5: return("UDMA 5");
case XFER_UDMA_4: return("UDMA 4");
case XFER_UDMA_3: return("UDMA 3");
case XFER_UDMA_2: return("UDMA 2");
case XFER_UDMA_1: return("UDMA 1");
case XFER_UDMA_0: return("UDMA 0");
case XFER_MW_DMA_2: return("MW DMA 2");
case XFER_MW_DMA_1: return("MW DMA 1");
case XFER_MW_DMA_0: return("MW DMA 0");
case XFER_SW_DMA_2: return("SW DMA 2");
case XFER_SW_DMA_1: return("SW DMA 1");
case XFER_SW_DMA_0: return("SW DMA 0");
case XFER_PIO_4: return("PIO 4");
case XFER_PIO_3: return("PIO 3");
case XFER_PIO_2: return("PIO 2");
case XFER_PIO_1: return("PIO 1");
case XFER_PIO_0: return("PIO 0");
case XFER_PIO_SLOW: return("PIO SLOW");
default: return("XFER ERROR");
}
}
/*
*
*/
char *ide_media_verbose (ide_drive_t *drive)
{
switch (drive->media) {
case ide_scsi: return("scsi ");
case ide_disk: return("disk ");
case ide_optical: return("optical");
case ide_cdrom: return("cdrom ");
case ide_tape: return("tape ");
case ide_floppy: return("floppy ");
default: return("???????");
}
}
/*
* A Verbose noise maker for debugging on the attempted dmaing calls.
*/
char *ide_dmafunc_verbose (ide_dma_action_t dmafunc)
{
switch (dmafunc) {
case ide_dma_read: return("ide_dma_read");
case ide_dma_write: return("ide_dma_write");
case ide_dma_begin: return("ide_dma_begin");
case ide_dma_end: return("ide_dma_end:");
case ide_dma_check: return("ide_dma_check");
case ide_dma_on: return("ide_dma_on");
case ide_dma_off: return("ide_dma_off");
case ide_dma_off_quietly: return("ide_dma_off_quietly");
case ide_dma_test_irq: return("ide_dma_test_irq");
case ide_dma_bad_drive: return("ide_dma_bad_drive");
case ide_dma_good_drive: return("ide_dma_good_drive");
case ide_dma_verbose: return("ide_dma_verbose");
case ide_dma_retune: return("ide_dma_retune");
case ide_dma_lostirq: return("ide_dma_lostirq");
case ide_dma_timeout: return("ide_dma_timeout");
default: return("unknown");
}
}
/*
*
*/
byte ide_auto_reduce_xfer (ide_drive_t *drive)
{
if (!drive->crc_count)
return drive->current_speed;
drive->crc_count = 0;
switch(drive->current_speed) {
case XFER_UDMA_7: return XFER_UDMA_6;
case XFER_UDMA_6: return XFER_UDMA_5;
case XFER_UDMA_5: return XFER_UDMA_4;
case XFER_UDMA_4: return XFER_UDMA_3;
case XFER_UDMA_3: return XFER_UDMA_2;
case XFER_UDMA_2: return XFER_UDMA_1;
case XFER_UDMA_1: return XFER_UDMA_0;
/*
* OOPS we do not goto non Ultra DMA modes
* without iCRC's available we force
* the system to PIO and make the user
* invoke the ATA-1 ATA-2 DMA modes.
*/
case XFER_UDMA_0:
default: return XFER_PIO_4;
}
}
/*
* Update the
*/
int ide_driveid_update (ide_drive_t *drive)
{
/*
* Re-read drive->id for possible DMA mode
* change (copied from ide-probe.c)
*/
struct hd_driveid *id;
unsigned long timeout, flags;
SELECT_MASK(HWIF(drive), drive, 1);
if (IDE_CONTROL_REG)
OUT_BYTE(drive->ctl,IDE_CONTROL_REG);
ide_delay_50ms();
OUT_BYTE(WIN_IDENTIFY, IDE_COMMAND_REG);
timeout = jiffies + WAIT_WORSTCASE;
do {
if (0 < (signed long)(jiffies - timeout)) {
SELECT_MASK(HWIF(drive), drive, 0);
return 0; /* drive timed-out */
}
ide_delay_50ms(); /* give drive a breather */
} while (IN_BYTE(IDE_ALTSTATUS_REG) & BUSY_STAT);
ide_delay_50ms(); /* wait for IRQ and DRQ_STAT */
if (!OK_STAT(GET_STAT(),DRQ_STAT,BAD_R_STAT)) {
SELECT_MASK(HWIF(drive), drive, 0);
printk("%s: CHECK for good STATUS\n", drive->name);
return 0;
}
__save_flags(flags); /* local CPU only */
__cli(); /* local CPU only; some systems need this */
SELECT_MASK(HWIF(drive), drive, 0);
id = kmalloc(SECTOR_WORDS*4, GFP_ATOMIC);
if (!id) {
__restore_flags(flags); /* local CPU only */
return 0;
}
ide_input_data(drive, id, SECTOR_WORDS);
(void) GET_STAT(); /* clear drive IRQ */
ide__sti(); /* local CPU only */
__restore_flags(flags); /* local CPU only */
ide_fix_driveid(id);
if (id) {
drive->id->dma_ultra = id->dma_ultra;
drive->id->dma_mword = id->dma_mword;
drive->id->dma_1word = id->dma_1word;
/* anything more ? */
kfree(id);
}
return 1;
}
/*
* Verify that we are doing an approved SETFEATURES_XFER with respect
* to the hardware being able to support request. Since some hardware
* can improperly report capabilties, we check to see if the host adapter
* in combination with the device (usually a disk) properly detect
* and acknowledge each end of the ribbon.
*/
int ide_ata66_check (ide_drive_t *drive, ide_task_t *args)
{
if ((args->tfRegister[IDE_COMMAND_OFFSET] == WIN_SETFEATURES) &&
(args->tfRegister[IDE_SECTOR_OFFSET] > XFER_UDMA_2) &&
(args->tfRegister[IDE_FEATURE_OFFSET] == SETFEATURES_XFER)) {
if (!HWIF(drive)->udma_four) {
printk("%s: Speed warnings UDMA 3/4/5 is not functional.\n", HWIF(drive)->name);
return 1;
}
#ifndef CONFIG_IDEDMA_IVB
if ((drive->id->hw_config & 0x6000) == 0) {
#else /* !CONFIG_IDEDMA_IVB */
if (((drive->id->hw_config & 0x2000) == 0) ||
((drive->id->hw_config & 0x4000) == 0)) {
#endif /* CONFIG_IDEDMA_IVB */
printk("%s: Speed warnings UDMA 3/4/5 is not functional.\n", drive->name);
return 1;
}
}
return 0;
}
/*
* Backside of HDIO_DRIVE_CMD call of SETFEATURES_XFER.
* 1 : Safe to update drive->id DMA registers.
* 0 : OOPs not allowed.
*/
int set_transfer (ide_drive_t *drive, ide_task_t *args)
{
if ((args->tfRegister[IDE_COMMAND_OFFSET] == WIN_SETFEATURES) &&
(args->tfRegister[IDE_SECTOR_OFFSET] >= XFER_SW_DMA_0) &&
(args->tfRegister[IDE_FEATURE_OFFSET] == SETFEATURES_XFER) &&
(drive->id->dma_ultra ||
drive->id->dma_mword ||
drive->id->dma_1word))
return 1;
return 0;
}
#ifdef CONFIG_BLK_DEV_IDEDMA
/*
* All hosts that use the 80c ribbon mus use!
*/
byte eighty_ninty_three (ide_drive_t *drive)
{
#ifdef CONFIG_BLK_DEV_IDEPCI
if (HWIF(drive)->pci_devid.vid==0x105a)
return(HWIF(drive)->udma_four);
#endif
/* PDC202XX: that's because some HDD will return wrong info */
return ((byte) ((HWIF(drive)->udma_four) &&
#ifndef CONFIG_IDEDMA_IVB
(drive->id->hw_config & 0x4000) &&
#endif /* CONFIG_IDEDMA_IVB */
(drive->id->hw_config & 0x6000)) ? 1 : 0);
}
#endif // CONFIG_BLK_DEV_IDEDMA
/*
* Similar to ide_wait_stat(), except it never calls ide_error internally.
* This is a kludge to handle the new ide_config_drive_speed() function,
* and should not otherwise be used anywhere. Eventually, the tuneproc's
* should be updated to return ide_startstop_t, in which case we can get
* rid of this abomination again. :) -ml
*
* It is gone..........
*
* const char *msg == consider adding for verbose errors.
*/
int ide_config_drive_speed (ide_drive_t *drive, byte speed)
{
ide_hwif_t *hwif = HWIF(drive);
int i, error = 1;
byte stat;
#if defined(CONFIG_BLK_DEV_IDEDMA) && !defined(CONFIG_DMA_NONPCI)
byte unit = (drive->select.b.unit & 0x01);
outb(inb(hwif->dma_base+2) & ~(1<<(5+unit)), hwif->dma_base+2);
#endif /* (CONFIG_BLK_DEV_IDEDMA) && !(CONFIG_DMA_NONPCI) */
/*
* Don't use ide_wait_cmd here - it will
* attempt to set_geometry and recalibrate,
* but for some reason these don't work at
* this point (lost interrupt).
*/
/*
* Select the drive, and issue the SETFEATURES command
*/
disable_irq(hwif->irq); /* disable_irq_nosync ?? */
udelay(1);
SELECT_DRIVE(HWIF(drive), drive);
SELECT_MASK(HWIF(drive), drive, 0);
udelay(1);
if (IDE_CONTROL_REG)
OUT_BYTE(drive->ctl | 2, IDE_CONTROL_REG);
OUT_BYTE(speed, IDE_NSECTOR_REG);
OUT_BYTE(SETFEATURES_XFER, IDE_FEATURE_REG);
OUT_BYTE(WIN_SETFEATURES, IDE_COMMAND_REG);
if ((IDE_CONTROL_REG) && (drive->quirk_list == 2))
OUT_BYTE(drive->ctl, IDE_CONTROL_REG);
udelay(1);
/*
* Wait for drive to become non-BUSY
*/
if ((stat = GET_STAT()) & BUSY_STAT) {
unsigned long flags, timeout;
__save_flags(flags); /* local CPU only */
ide__sti(); /* local CPU only -- for jiffies */
timeout = jiffies + WAIT_CMD;
while ((stat = GET_STAT()) & BUSY_STAT) {
if (0 < (signed long)(jiffies - timeout))
break;
}
__restore_flags(flags); /* local CPU only */
}
/*
* Allow status to settle, then read it again.
* A few rare drives vastly violate the 400ns spec here,
* so we'll wait up to 10usec for a "good" status
* rather than expensively fail things immediately.
* This fix courtesy of Matthew Faupel & Niccolo Rigacci.
*/
for (i = 0; i < 10; i++) {
udelay(1);
if (OK_STAT((stat = GET_STAT()), DRIVE_READY, BUSY_STAT|DRQ_STAT|ERR_STAT)) {
error = 0;
break;
}
}
SELECT_MASK(HWIF(drive), drive, 0);
enable_irq(hwif->irq);
if (error) {
(void) ide_dump_status(drive, "set_drive_speed_status", stat);
return error;
}
drive->id->dma_ultra &= ~0xFF00;
drive->id->dma_mword &= ~0x0F00;
drive->id->dma_1word &= ~0x0F00;
#if defined(CONFIG_BLK_DEV_IDEDMA) && !defined(CONFIG_DMA_NONPCI)
if (speed > XFER_PIO_4) {
outb(inb(hwif->dma_base+2)|(1<<(5+unit)), hwif->dma_base+2);
} else {
outb(inb(hwif->dma_base+2) & ~(1<<(5+unit)), hwif->dma_base+2);
}
#endif /* (CONFIG_BLK_DEV_IDEDMA) && !(CONFIG_DMA_NONPCI) */
switch(speed) {
case XFER_UDMA_7: drive->id->dma_ultra |= 0x8080; break;
case XFER_UDMA_6: drive->id->dma_ultra |= 0x4040; break;
case XFER_UDMA_5: drive->id->dma_ultra |= 0x2020; break;
case XFER_UDMA_4: drive->id->dma_ultra |= 0x1010; break;
case XFER_UDMA_3: drive->id->dma_ultra |= 0x0808; break;
case XFER_UDMA_2: drive->id->dma_ultra |= 0x0404; break;
case XFER_UDMA_1: drive->id->dma_ultra |= 0x0202; break;
case XFER_UDMA_0: drive->id->dma_ultra |= 0x0101; break;
case XFER_MW_DMA_2: drive->id->dma_mword |= 0x0404; break;
case XFER_MW_DMA_1: drive->id->dma_mword |= 0x0202; break;
case XFER_MW_DMA_0: drive->id->dma_mword |= 0x0101; break;
case XFER_SW_DMA_2: drive->id->dma_1word |= 0x0404; break;
case XFER_SW_DMA_1: drive->id->dma_1word |= 0x0202; break;
case XFER_SW_DMA_0: drive->id->dma_1word |= 0x0101; break;
default: break;
}
return error;
}
EXPORT_SYMBOL(ide_auto_reduce_xfer);
EXPORT_SYMBOL(ide_driveid_update);
EXPORT_SYMBOL(ide_ata66_check);
EXPORT_SYMBOL(set_transfer);
#ifdef CONFIG_BLK_DEV_IDEDMA
EXPORT_SYMBOL(eighty_ninty_three);
#endif // CONFIG_BLK_DEV_IDEDMA
EXPORT_SYMBOL(ide_config_drive_speed);
+222
View File
@@ -0,0 +1,222 @@
/*
* linux/drivers/ide/ide-geometry.c
*/
#include <xeno/config.h>
#include <xeno/ide.h>
#include <asm/mc146818rtc.h>
#include <asm/io.h>
#ifdef CONFIG_BLK_DEV_IDE
/*
* We query CMOS about hard disks : it could be that we have a SCSI/ESDI/etc
* controller that is BIOS compatible with ST-506, and thus showing up in our
* BIOS table, but not register compatible, and therefore not present in CMOS.
*
* Furthermore, we will assume that our ST-506 drives <if any> are the primary
* drives in the system -- the ones reflected as drive 1 or 2. The first
* drive is stored in the high nibble of CMOS byte 0x12, the second in the low
* nibble. This will be either a 4 bit drive type or 0xf indicating use byte
* 0x19 for an 8 bit type, drive 1, 0x1a for drive 2 in CMOS. A non-zero value
* means we have an AT controller hard disk for that drive.
*
* Of course, there is no guarantee that either drive is actually on the
* "primary" IDE interface, but we don't bother trying to sort that out here.
* If a drive is not actually on the primary interface, then these parameters
* will be ignored. This results in the user having to supply the logical
* drive geometry as a boot parameter for each drive not on the primary i/f.
*/
/*
* The only "perfect" way to handle this would be to modify the setup.[cS] code
* to do BIOS calls Int13h/Fn08h and Int13h/Fn48h to get all of the drive info
* for us during initialization. I have the necessary docs -- any takers? -ml
*/
/*
* I did this, but it doesnt work - there is no reasonable way to find the
* correspondence between the BIOS numbering of the disks and the Linux
* numbering. -aeb
*
* The code below is bad. One of the problems is that drives 1 and 2
* may be SCSI disks (even when IDE disks are present), so that
* the geometry we read here from BIOS is attributed to the wrong disks.
* Consequently, also the former "drive->present = 1" below was a mistake.
*
* Eventually the entire routine below should be removed.
*
* 17-OCT-2000 rjohnson@analogic.com Added spin-locks for reading CMOS
* chip.
*/
void probe_cmos_for_drives (ide_hwif_t *hwif)
{
#ifdef __i386__
extern struct drive_info_struct drive_info;
byte cmos_disks, *BIOS = (byte *) &drive_info;
int unit;
unsigned long flags;
#ifdef CONFIG_BLK_DEV_PDC4030
if (hwif->chipset == ide_pdc4030 && hwif->channel != 0)
return;
#endif /* CONFIG_BLK_DEV_PDC4030 */
spin_lock_irqsave(&rtc_lock, flags);
cmos_disks = CMOS_READ(0x12);
spin_unlock_irqrestore(&rtc_lock, flags);
/* Extract drive geometry from CMOS+BIOS if not already setup */
for (unit = 0; unit < MAX_DRIVES; ++unit) {
ide_drive_t *drive = &hwif->drives[unit];
if ((cmos_disks & (0xf0 >> (unit*4)))
&& !drive->present && !drive->nobios) {
unsigned short cyl = *(unsigned short *)BIOS;
unsigned char head = *(BIOS+2);
unsigned char sect = *(BIOS+14);
if (cyl > 0 && head > 0 && sect > 0 && sect < 64) {
drive->cyl = drive->bios_cyl = cyl;
drive->head = drive->bios_head = head;
drive->sect = drive->bios_sect = sect;
drive->ctl = *(BIOS+8);
} else {
printk("hd%c: C/H/S=%d/%d/%d from BIOS ignored\n",
unit+'a', cyl, head, sect);
}
}
BIOS += 16;
}
#endif
}
#endif /* CONFIG_BLK_DEV_IDE */
#if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE)
extern ide_drive_t * get_info_ptr(kdev_t);
extern unsigned long current_capacity (ide_drive_t *);
/*
* If heads is nonzero: find a translation with this many heads and S=63.
* Otherwise: find out how OnTrack Disk Manager would translate the disk.
*/
static void
ontrack(ide_drive_t *drive, int heads, unsigned int *c, int *h, int *s) {
static const byte dm_head_vals[] = {4, 8, 16, 32, 64, 128, 255, 0};
const byte *headp = dm_head_vals;
unsigned long total;
/*
* The specs say: take geometry as obtained from Identify,
* compute total capacity C*H*S from that, and truncate to
* 1024*255*63. Now take S=63, H the first in the sequence
* 4, 8, 16, 32, 64, 128, 255 such that 63*H*1024 >= total.
* [Please tell aeb@cwi.nl in case this computes a
* geometry different from what OnTrack uses.]
*/
total = DRIVER(drive)->capacity(drive);
*s = 63;
if (heads) {
*h = heads;
*c = total / (63 * heads);
return;
}
while (63 * headp[0] * 1024 < total && headp[1] != 0)
headp++;
*h = headp[0];
*c = total / (63 * headp[0]);
}
/*
* This routine is called from the partition-table code in pt/msdos.c.
* It has two tasks:
* (i) to handle Ontrack DiskManager by offsetting everything by 63 sectors,
* or to handle EZdrive by remapping sector 0 to sector 1.
* (ii) to invent a translated geometry.
* Part (i) is suppressed if the user specifies the "noremap" option
* on the command line.
* Part (ii) is suppressed if the user specifies an explicit geometry.
*
* The ptheads parameter is either 0 or tells about the number of
* heads shown by the end of the first nonempty partition.
* If this is either 16, 32, 64, 128, 240 or 255 we'll believe it.
*
* The xparm parameter has the following meaning:
* 0 = convert to CHS with fewer than 1024 cyls
* using the same method as Ontrack DiskManager.
* 1 = same as "0", plus offset everything by 63 sectors.
* -1 = similar to "0", plus redirect sector 0 to sector 1.
* 2 = convert to a CHS geometry with "ptheads" heads.
*
* Returns 0 if the translation was not possible, if the device was not
* an IDE disk drive, or if a geometry was "forced" on the commandline.
* Returns 1 if the geometry translation was successful.
*/
int ide_xlate_1024 (kdev_t i_rdev, int xparm, int ptheads, const char *msg)
{
ide_drive_t *drive;
const char *msg1 = "";
int heads = 0;
int c, h, s;
int transl = 1; /* try translation */
int ret = 0;
drive = get_info_ptr(i_rdev);
if (!drive)
return 0;
/* remap? */
if (drive->remap_0_to_1 != 2) {
if (xparm == 1) { /* DM */
drive->sect0 = 63;
msg1 = " [remap +63]";
ret = 1;
} else if (xparm == -1) { /* EZ-Drive */
if (drive->remap_0_to_1 == 0) {
drive->remap_0_to_1 = 1;
msg1 = " [remap 0->1]";
ret = 1;
}
}
}
/* There used to be code here that assigned drive->id->CHS
to drive->CHS and that to drive->bios_CHS. However,
some disks have id->C/H/S = 4092/16/63 but are larger than 2.1 GB.
In such cases that code was wrong. Moreover,
there seems to be no reason to do any of these things. */
/* translate? */
if (drive->forced_geom)
transl = 0;
/* does ptheads look reasonable? */
if (ptheads == 32 || ptheads == 64 || ptheads == 128 ||
ptheads == 240 || ptheads == 255)
heads = ptheads;
if (xparm == 2) {
if (!heads ||
(drive->bios_head >= heads && drive->bios_sect == 63))
transl = 0;
}
if (xparm == -1) {
if (drive->bios_head > 16)
transl = 0; /* we already have a translation */
}
if (transl) {
ontrack(drive, heads, &c, &h, &s);
drive->bios_cyl = c;
drive->bios_head = h;
drive->bios_sect = s;
ret = 1;
}
drive->part[0].nr_sects = current_capacity(drive);
if (ret)
printk("%s%s [%d/%d/%d]", msg, msg1,
drive->bios_cyl, drive->bios_head, drive->bios_sect);
return ret;
}
#endif /* defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE) */
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+236
View File
@@ -0,0 +1,236 @@
/*
* linux/drivers/ide/ide_modes.h
*
* Copyright (C) 1996 Linus Torvalds, Igor Abramov, and Mark Lord
*/
#ifndef _IDE_MODES_H
#define _IDE_MODES_H
#include <linux/config.h>
/*
* Shared data/functions for determining best PIO mode for an IDE drive.
* Most of this stuff originally lived in cmd640.c, and changes to the
* ide_pio_blacklist[] table should be made with EXTREME CAUTION to avoid
* breaking the fragile cmd640.c support.
*/
#ifdef CONFIG_BLK_DEV_IDE_MODES
/*
* Standard (generic) timings for PIO modes, from ATA2 specification.
* These timings are for access to the IDE data port register *only*.
* Some drives may specify a mode, while also specifying a different
* value for cycle_time (from drive identification data).
*/
typedef struct ide_pio_timings_s {
int setup_time; /* Address setup (ns) minimum */
int active_time; /* Active pulse (ns) minimum */
int cycle_time; /* Cycle time (ns) minimum = (setup + active + recovery) */
} ide_pio_timings_t;
typedef struct ide_pio_data_s {
byte pio_mode;
byte use_iordy;
byte overridden;
byte blacklisted;
unsigned int cycle_time;
} ide_pio_data_t;
#ifndef _IDE_C
int ide_scan_pio_blacklist (char *model);
byte ide_get_best_pio_mode (ide_drive_t *drive, byte mode_wanted, byte max_mode, ide_pio_data_t *d);
extern const ide_pio_timings_t ide_pio_timings[6];
#else /* _IDE_C */
const ide_pio_timings_t ide_pio_timings[6] = {
{ 70, 165, 600 }, /* PIO Mode 0 */
{ 50, 125, 383 }, /* PIO Mode 1 */
{ 30, 100, 240 }, /* PIO Mode 2 */
{ 30, 80, 180 }, /* PIO Mode 3 with IORDY */
{ 25, 70, 120 }, /* PIO Mode 4 with IORDY */
{ 20, 50, 100 } /* PIO Mode 5 with IORDY (nonstandard) */
};
/*
* Black list. Some drives incorrectly report their maximal PIO mode,
* at least in respect to CMD640. Here we keep info on some known drives.
*/
static struct ide_pio_info {
const char *name;
int pio;
} ide_pio_blacklist [] = {
/* { "Conner Peripherals 1275MB - CFS1275A", 4 }, */
{ "Conner Peripherals 540MB - CFS540A", 3 },
{ "WDC AC2700", 3 },
{ "WDC AC2540", 3 },
{ "WDC AC2420", 3 },
{ "WDC AC2340", 3 },
{ "WDC AC2250", 0 },
{ "WDC AC2200", 0 },
{ "WDC AC21200", 4 },
{ "WDC AC2120", 0 },
{ "WDC AC2850", 3 },
{ "WDC AC1270", 3 },
{ "WDC AC1170", 1 },
{ "WDC AC1210", 1 },
{ "WDC AC280", 0 },
/* { "WDC AC21000", 4 }, */
{ "WDC AC31000", 3 },
{ "WDC AC31200", 3 },
/* { "WDC AC31600", 4 }, */
{ "Maxtor 7131 AT", 1 },
{ "Maxtor 7171 AT", 1 },
{ "Maxtor 7213 AT", 1 },
{ "Maxtor 7245 AT", 1 },
{ "Maxtor 7345 AT", 1 },
{ "Maxtor 7546 AT", 3 },
{ "Maxtor 7540 AV", 3 },
{ "SAMSUNG SHD-3121A", 1 },
{ "SAMSUNG SHD-3122A", 1 },
{ "SAMSUNG SHD-3172A", 1 },
/* { "ST51080A", 4 },
* { "ST51270A", 4 },
* { "ST31220A", 4 },
* { "ST31640A", 4 },
* { "ST32140A", 4 },
* { "ST3780A", 4 },
*/
{ "ST5660A", 3 },
{ "ST3660A", 3 },
{ "ST3630A", 3 },
{ "ST3655A", 3 },
{ "ST3391A", 3 },
{ "ST3390A", 1 },
{ "ST3600A", 1 },
{ "ST3290A", 0 },
{ "ST3144A", 0 },
{ "ST3491A", 1 }, /* reports 3, should be 1 or 2 (depending on */
/* drive) according to Seagates FIND-ATA program */
{ "QUANTUM ELS127A", 0 },
{ "QUANTUM ELS170A", 0 },
{ "QUANTUM LPS240A", 0 },
{ "QUANTUM LPS210A", 3 },
{ "QUANTUM LPS270A", 3 },
{ "QUANTUM LPS365A", 3 },
{ "QUANTUM LPS540A", 3 },
{ "QUANTUM LIGHTNING 540A", 3 },
{ "QUANTUM LIGHTNING 730A", 3 },
{ "QUANTUM FIREBALL_540", 3 }, /* Older Quantum Fireballs don't work */
{ "QUANTUM FIREBALL_640", 3 },
{ "QUANTUM FIREBALL_1080", 3 },
{ "QUANTUM FIREBALL_1280", 3 },
{ NULL, 0 }
};
/*
* This routine searches the ide_pio_blacklist for an entry
* matching the start/whole of the supplied model name.
*
* Returns -1 if no match found.
* Otherwise returns the recommended PIO mode from ide_pio_blacklist[].
*/
int ide_scan_pio_blacklist (char *model)
{
struct ide_pio_info *p;
for (p = ide_pio_blacklist; p->name != NULL; p++) {
if (strncmp(p->name, model, strlen(p->name)) == 0)
return p->pio;
}
return -1;
}
/*
* This routine returns the recommended PIO settings for a given drive,
* based on the drive->id information and the ide_pio_blacklist[].
* This is used by most chipset support modules when "auto-tuning".
*/
/*
* Drive PIO mode auto selection
*/
byte ide_get_best_pio_mode (ide_drive_t *drive, byte mode_wanted, byte max_mode, ide_pio_data_t *d)
{
int pio_mode;
int cycle_time = 0;
int use_iordy = 0;
struct hd_driveid* id = drive->id;
int overridden = 0;
int blacklisted = 0;
if (mode_wanted != 255) {
pio_mode = mode_wanted;
} else if (!drive->id) {
pio_mode = 0;
} else if ((pio_mode = ide_scan_pio_blacklist(id->model)) != -1) {
overridden = 1;
blacklisted = 1;
use_iordy = (pio_mode > 2);
} else {
pio_mode = id->tPIO;
if (pio_mode > 2) { /* 2 is maximum allowed tPIO value */
pio_mode = 2;
overridden = 1;
}
if (id->field_valid & 2) { /* drive implements ATA2? */
if (id->capability & 8) { /* drive supports use_iordy? */
use_iordy = 1;
cycle_time = id->eide_pio_iordy;
if (id->eide_pio_modes & 7) {
overridden = 0;
if (id->eide_pio_modes & 4)
pio_mode = 5;
else if (id->eide_pio_modes & 2)
pio_mode = 4;
else
pio_mode = 3;
}
} else {
cycle_time = id->eide_pio;
}
}
#if 0
if (drive->id->major_rev_num & 0x0004) printk("ATA-2 ");
#endif
/*
* Conservative "downgrade" for all pre-ATA2 drives
*/
if (pio_mode && pio_mode < 4) {
pio_mode--;
overridden = 1;
#if 0
use_iordy = (pio_mode > 2);
#endif
if (cycle_time && cycle_time < ide_pio_timings[pio_mode].cycle_time)
cycle_time = 0; /* use standard timing */
}
}
if (pio_mode > max_mode) {
pio_mode = max_mode;
cycle_time = 0;
}
if (d) {
d->pio_mode = pio_mode;
d->cycle_time = cycle_time ? cycle_time : ide_pio_timings[pio_mode].cycle_time;
d->use_iordy = use_iordy;
d->overridden = overridden;
d->blacklisted = blacklisted;
}
return pio_mode;
}
#endif /* _IDE_C */
#endif /* CONFIG_BLK_DEV_IDE_MODES */
#endif /* _IDE_MODES_H */
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+10
View File
@@ -0,0 +1,10 @@
include $(BASEDIR)/Rules.mk
default: $(OBJS)
$(MAKE) -C tulip
$(LD) -r -o driver.o tulip/tulip.o $(OBJS)
clean:
$(MAKE) -C tulip clean
rm -f *.o *~ core
+44
View File
@@ -0,0 +1,44 @@
/*
* INET An implementation of the TCP/IP protocol suite for the LINUX
* operating system. INET is implemented using the BSD Socket
* interface as the means of communication with the user level.
*
* Holds initial configuration information for devices.
*
* Version: @(#)Space.c 1.0.7 08/12/93
*
* Authors: Ross Biro, <bir7@leland.Stanford.Edu>
* Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
* Donald J. Becker, <becker@scyld.com>
*/
#include <linux/config.h>
#include <linux/netdevice.h>
/*
* KAF (23/7/02): All the probe shit is gone from here -- each network
* driver should probe as part of its setup, and dynamically append
* to dev_base when it finds a NIC.
*/
/*
* The @dev_base list is protected by @dev_base_lock and the rtln
* semaphore.
*
* Pure readers hold dev_base_lock for reading.
*
* Writers must hold the rtnl semaphore while they loop through the
* dev_base list, and hold dev_base_lock for writing when they do the
* actual updates. This allows pure readers to access the list even
* while a writer is preparing to update it.
*
* To put it another way, dev_base_lock is held for writing only to
* protect against pure readers; the rtnl semaphore provides the
* protection against other writers.
*
* See, for example usages, register_netdevice() and
* unregister_netdevice(), which must be called with the rtnl
* semaphore held.
*/
struct net_device *dev_base = NULL;
rwlock_t dev_base_lock = RW_LOCK_UNLOCKED;
File diff suppressed because it is too large. Load diff
+738
View File
@@ -0,0 +1,738 @@
/* net_init.c: Initialization for network devices. */
/*
Written 1993,1994,1995 by Donald Becker.
The author may be reached as becker@scyld.com, or C/O
Scyld Computing Corporation
410 Severn Ave., Suite 210
Annapolis MD 21403
This file contains the initialization for the "pl14+" style ethernet
drivers. It should eventually replace most of drivers/net/Space.c.
It's primary advantage is that it's able to allocate low-memory buffers.
A secondary advantage is that the dangerous NE*000 netcards can reserve
their I/O port region before the SCSI probes start.
Modifications/additions by Bjorn Ekwall <bj0rn@blox.se>:
ethdev_index[MAX_ETH_CARDS]
register_netdev() / unregister_netdev()
Modifications by Wolfgang Walter
Use dev_close cleanly so we always shut things down tidily.
Changed 29/10/95, Alan Cox to pass sockaddr's around for mac addresses.
14/06/96 - Paul Gortmaker: Add generic eth_change_mtu() function.
24/09/96 - Paul Norton: Add token-ring variants of the netdev functions.
08/11/99 - Alan Cox: Got fed up of the mess in this file and cleaned it
up. We now share common code and have regularised name
allocation setups. Abolished the 16 card limits.
03/19/2000 - jgarzik and Urban Widmark: init_etherdev 32-byte align
03/21/2001 - jgarzik: alloc_etherdev and friends
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/errno.h>
//#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/types.h>
//#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/if_ether.h>
#include <linux/lib.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
//#include <linux/fddidevice.h>
//#include <linux/hippidevice.h>
//#include <linux/trdevice.h>
//#include <linux/fcdevice.h>
//#include <linux/if_arp.h>
//#include <linux/if_ltalk.h>
//#include <linux/rtnetlink.h>
//#include <net/neighbour.h>
#define rtnl_lock() ((void)0)
#define rtnl_unlock() ((void)0)
/* The network devices currently exist only in the socket namespace, so these
entries are unused. The only ones that make sense are
open start the ethercard
close stop the ethercard
ioctl To get statistics, perhaps set the interface port (AUI, BNC, etc.)
One can also imagine getting raw packets using
read & write
but this is probably better handled by a raw packet socket.
Given that almost all of these functions are handled in the current
socket-based scheme, putting ethercard devices in /dev/ seems pointless.
[Removed all support for /dev network devices. When someone adds
streams then by magic we get them, but otherwise they are un-needed
and a space waste]
*/
static struct net_device *alloc_netdev(int sizeof_priv, const char *mask,
void (*setup)(struct net_device *))
{
struct net_device *dev;
int alloc_size;
/* ensure 32-byte alignment of the private area */
alloc_size = sizeof (*dev) + sizeof_priv + 31;
dev = (struct net_device *) kmalloc (alloc_size, GFP_KERNEL);
if (dev == NULL)
{
printk(KERN_ERR "alloc_dev: Unable to allocate device memory.\n");
return NULL;
}
memset(dev, 0, alloc_size);
if (sizeof_priv)
dev->priv = (void *) (((long)(dev + 1) + 31) & ~31);
setup(dev);
strcpy(dev->name, mask);
return dev;
}
static struct net_device *init_alloc_dev(int sizeof_priv)
{
struct net_device *dev;
int alloc_size;
/* ensure 32-byte alignment of the private area */
alloc_size = sizeof (*dev) + sizeof_priv + 31;
dev = (struct net_device *) kmalloc (alloc_size, GFP_KERNEL);
if (dev == NULL)
{
printk(KERN_ERR "alloc_dev: Unable to allocate device memory.\n");
return NULL;
}
memset(dev, 0, alloc_size);
if (sizeof_priv)
dev->priv = (void *) (((long)(dev + 1) + 31) & ~31);
return dev;
}
/*
* Create and name a device from a prototype, then perform any needed
* setup.
*/
static struct net_device *init_netdev(struct net_device *dev, int sizeof_priv,
char *mask, void (*setup)(struct net_device *))
{
int new_device = 0;
/*
* Allocate a device if one is not provided.
*/
if (dev == NULL) {
dev=init_alloc_dev(sizeof_priv);
if(dev==NULL)
return NULL;
new_device = 1;
}
/*
* Allocate a name
*/
if (dev->name[0] == '\0' || dev->name[0] == ' ') {
strcpy(dev->name, mask);
if (dev_alloc_name(dev, mask)<0) {
if (new_device)
kfree(dev);
return NULL;
}
}
//netdev_boot_setup_check(dev);
/*
* Configure via the caller provided setup function then
* register if needed.
*/
setup(dev);
if (new_device) {
int err;
rtnl_lock();
err = register_netdevice(dev);
rtnl_unlock();
if (err < 0) {
kfree(dev);
dev = NULL;
}
}
return dev;
}
#if defined(CONFIG_HIPPI) || defined(CONFIG_TR) || defined(CONFIG_NET_FC)
static int __register_netdev(struct net_device *dev)
{
if (dev->init && dev->init(dev) != 0) {
unregister_netdev(dev);
return -EIO;
}
return 0;
}
#endif
/**
* init_etherdev - Register ethernet device
* @dev: An ethernet device structure to be filled in, or %NULL if a new
* struct should be allocated.
* @sizeof_priv: Size of additional driver-private structure to be allocated
* for this ethernet device
*
* Fill in the fields of the device structure with ethernet-generic values.
*
* If no device structure is passed, a new one is constructed, complete with
* a private data area of size @sizeof_priv. A 32-byte (not bit)
* alignment is enforced for this private data area.
*
* If an empty string area is passed as dev->name, or a new structure is made,
* a new name string is constructed.
*/
struct net_device *init_etherdev(struct net_device *dev, int sizeof_priv)
{
return init_netdev(dev, sizeof_priv, "eth%d", ether_setup);
}
/**
* alloc_etherdev - Allocates and sets up an ethernet device
* @sizeof_priv: Size of additional driver-private structure to be allocated
* for this ethernet device
*
* Fill in the fields of the device structure with ethernet-generic
* values. Basically does everything except registering the device.
*
* Constructs a new net device, complete with a private data area of
* size @sizeof_priv. A 32-byte (not bit) alignment is enforced for
* this private data area.
*/
struct net_device *alloc_etherdev(int sizeof_priv)
{
return alloc_netdev(sizeof_priv, "eth%d", ether_setup);
}
EXPORT_SYMBOL(init_etherdev);
EXPORT_SYMBOL(alloc_etherdev);
static int eth_mac_addr(struct net_device *dev, void *p)
{
struct sockaddr *addr=p;
if (netif_running(dev))
return -EBUSY;
memcpy(dev->dev_addr, addr->sa_data,dev->addr_len);
return 0;
}
static int eth_change_mtu(struct net_device *dev, int new_mtu)
{
if ((new_mtu < 68) || (new_mtu > 1500))
return -EINVAL;
dev->mtu = new_mtu;
return 0;
}
#ifdef CONFIG_FDDI
/**
* init_fddidev - Register FDDI device
* @dev: A FDDI device structure to be filled in, or %NULL if a new
* struct should be allocated.
* @sizeof_priv: Size of additional driver-private structure to be allocated
* for this ethernet device
*
* Fill in the fields of the device structure with FDDI-generic values.
*
* If no device structure is passed, a new one is constructed, complete with
* a private data area of size @sizeof_priv. A 32-byte (not bit)
* alignment is enforced for this private data area.
*
* If an empty string area is passed as dev->name, or a new structure is made,
* a new name string is constructed.
*/
struct net_device *init_fddidev(struct net_device *dev, int sizeof_priv)
{
return init_netdev(dev, sizeof_priv, "fddi%d", fddi_setup);
}
/**
* alloc_fddidev - Register FDDI device
* @sizeof_priv: Size of additional driver-private structure to be allocated
* for this FDDI device
*
* Fill in the fields of the device structure with FDDI-generic values.
*
* Constructs a new net device, complete with a private data area of
* size @sizeof_priv. A 32-byte (not bit) alignment is enforced for
* this private data area.
*/
struct net_device *alloc_fddidev(int sizeof_priv)
{
return alloc_netdev(sizeof_priv, "fddi%d", fddi_setup);
}
EXPORT_SYMBOL(init_fddidev);
EXPORT_SYMBOL(alloc_fddidev);
static int fddi_change_mtu(struct net_device *dev, int new_mtu)
{
if ((new_mtu < FDDI_K_SNAP_HLEN) || (new_mtu > FDDI_K_SNAP_DLEN))
return(-EINVAL);
dev->mtu = new_mtu;
return(0);
}
#endif /* CONFIG_FDDI */
#ifdef CONFIG_HIPPI
static int hippi_change_mtu(struct net_device *dev, int new_mtu)
{
/*
* HIPPI's got these nice large MTUs.
*/
if ((new_mtu < 68) || (new_mtu > 65280))
return -EINVAL;
dev->mtu = new_mtu;
return(0);
}
/*
* For HIPPI we will actually use the lower 4 bytes of the hardware
* address as the I-FIELD rather than the actual hardware address.
*/
static int hippi_mac_addr(struct net_device *dev, void *p)
{
struct sockaddr *addr = p;
if (netif_running(dev))
return -EBUSY;
memcpy(dev->dev_addr, addr->sa_data, dev->addr_len);
return 0;
}
/**
* init_hippi_dev - Register HIPPI device
* @dev: A HIPPI device structure to be filled in, or %NULL if a new
* struct should be allocated.
* @sizeof_priv: Size of additional driver-private structure to be allocated
* for this ethernet device
*
* Fill in the fields of the device structure with HIPPI-generic values.
*
* If no device structure is passed, a new one is constructed, complete with
* a private data area of size @sizeof_priv. A 32-byte (not bit)
* alignment is enforced for this private data area.
*
* If an empty string area is passed as dev->name, or a new structure is made,
* a new name string is constructed.
*/
struct net_device *init_hippi_dev(struct net_device *dev, int sizeof_priv)
{
return init_netdev(dev, sizeof_priv, "hip%d", hippi_setup);
}
/**
* alloc_hippi_dev - Register HIPPI device
* @sizeof_priv: Size of additional driver-private structure to be allocated
* for this HIPPI device
*
* Fill in the fields of the device structure with HIPPI-generic values.
*
* Constructs a new net device, complete with a private data area of
* size @sizeof_priv. A 32-byte (not bit) alignment is enforced for
* this private data area.
*/
struct net_device *alloc_hippi_dev(int sizeof_priv)
{
return alloc_netdev(sizeof_priv, "hip%d", hippi_setup);
}
int register_hipdev(struct net_device *dev)
{
return __register_netdev(dev);
}
void unregister_hipdev(struct net_device *dev)
{
unregister_netdev(dev);
}
EXPORT_SYMBOL(init_hippi_dev);
EXPORT_SYMBOL(alloc_hippi_dev);
EXPORT_SYMBOL(register_hipdev);
EXPORT_SYMBOL(unregister_hipdev);
static int hippi_neigh_setup_dev(struct net_device *dev, struct neigh_parms *p)
{
/* Never send broadcast/multicast ARP messages */
p->mcast_probes = 0;
/* In IPv6 unicast probes are valid even on NBMA,
* because they are encapsulated in normal IPv6 protocol.
* Should be a generic flag.
*/
if (p->tbl->family != AF_INET6)
p->ucast_probes = 0;
return 0;
}
#endif /* CONFIG_HIPPI */
void ether_setup(struct net_device *dev)
{
/* Fill in the fields of the device structure with ethernet-generic values.
This should be in a common file instead of per-driver. */
dev->change_mtu = eth_change_mtu;
dev->hard_header = eth_header;
dev->rebuild_header = eth_rebuild_header;
dev->set_mac_address = eth_mac_addr;
dev->hard_header_cache = eth_header_cache;
dev->header_cache_update= eth_header_cache_update;
dev->hard_header_parse = eth_header_parse;
dev->type = 0; //ARPHRD_ETHER;
dev->hard_header_len = ETH_HLEN;
dev->mtu = 1500; /* eth_mtu */
dev->addr_len = ETH_ALEN;
dev->tx_queue_len = 100; /* Ethernet wants good queues */
memset(dev->broadcast,0xFF, ETH_ALEN);
/* New-style flags. */
dev->flags = IFF_BROADCAST|IFF_MULTICAST;
}
EXPORT_SYMBOL(ether_setup);
#ifdef CONFIG_FDDI
void fddi_setup(struct net_device *dev)
{
/*
* Fill in the fields of the device structure with FDDI-generic values.
* This should be in a common file instead of per-driver.
*/
dev->change_mtu = fddi_change_mtu;
dev->hard_header = fddi_header;
dev->rebuild_header = fddi_rebuild_header;
dev->type = ARPHRD_FDDI;
dev->hard_header_len = FDDI_K_SNAP_HLEN+3; /* Assume 802.2 SNAP hdr len + 3 pad bytes */
dev->mtu = FDDI_K_SNAP_DLEN; /* Assume max payload of 802.2 SNAP frame */
dev->addr_len = FDDI_K_ALEN;
dev->tx_queue_len = 100; /* Long queues on FDDI */
memset(dev->broadcast, 0xFF, FDDI_K_ALEN);
/* New-style flags */
dev->flags = IFF_BROADCAST | IFF_MULTICAST;
}
EXPORT_SYMBOL(fddi_setup);
#endif /* CONFIG_FDDI */
#ifdef CONFIG_HIPPI
void hippi_setup(struct net_device *dev)
{
dev->set_multicast_list = NULL;
dev->change_mtu = hippi_change_mtu;
dev->hard_header = hippi_header;
dev->rebuild_header = hippi_rebuild_header;
dev->set_mac_address = hippi_mac_addr;
dev->hard_header_parse = NULL;
dev->hard_header_cache = NULL;
dev->header_cache_update = NULL;
dev->neigh_setup = hippi_neigh_setup_dev;
/*
* We don't support HIPPI `ARP' for the time being, and probably
* never will unless someone else implements it. However we
* still need a fake ARPHRD to make ifconfig and friends play ball.
*/
dev->type = ARPHRD_HIPPI;
dev->hard_header_len = HIPPI_HLEN;
dev->mtu = 65280;
dev->addr_len = HIPPI_ALEN;
dev->tx_queue_len = 25 /* 5 */;
memset(dev->broadcast, 0xFF, HIPPI_ALEN);
/*
* HIPPI doesn't support broadcast+multicast and we only use
* static ARP tables. ARP is disabled by hippi_neigh_setup_dev.
*/
dev->flags = 0;
}
EXPORT_SYMBOL(hippi_setup);
#endif /* CONFIG_HIPPI */
#if defined(CONFIG_ATALK) || defined(CONFIG_ATALK_MODULE)
static int ltalk_change_mtu(struct net_device *dev, int mtu)
{
return -EINVAL;
}
static int ltalk_mac_addr(struct net_device *dev, void *addr)
{
return -EINVAL;
}
void ltalk_setup(struct net_device *dev)
{
/* Fill in the fields of the device structure with localtalk-generic values. */
dev->change_mtu = ltalk_change_mtu;
dev->hard_header = NULL;
dev->rebuild_header = NULL;
dev->set_mac_address = ltalk_mac_addr;
dev->hard_header_cache = NULL;
dev->header_cache_update= NULL;
dev->type = ARPHRD_LOCALTLK;
dev->hard_header_len = LTALK_HLEN;
dev->mtu = LTALK_MTU;
dev->addr_len = LTALK_ALEN;
dev->tx_queue_len = 10;
dev->broadcast[0] = 0xFF;
dev->flags = IFF_BROADCAST|IFF_MULTICAST|IFF_NOARP;
}
EXPORT_SYMBOL(ltalk_setup);
#endif /* CONFIG_ATALK || CONFIG_ATALK_MODULE */
int register_netdev(struct net_device *dev)
{
int err;
rtnl_lock();
/*
* If the name is a format string the caller wants us to
* do a name allocation
*/
if (strchr(dev->name, '%'))
{
err = dev_alloc_name(dev, dev->name);
if (err < 0)
goto out;
}
/*
* Back compatibility hook. Kill this one in 2.5
*/
if (dev->name[0]==0 || dev->name[0]==' ')
{
err = dev_alloc_name(dev, "eth%d");
if (err < 0)
goto out;
}
err = register_netdevice(dev);
out:
rtnl_unlock();
return err;
}
void unregister_netdev(struct net_device *dev)
{
rtnl_lock();
unregister_netdevice(dev);
rtnl_unlock();
}
EXPORT_SYMBOL(register_netdev);
EXPORT_SYMBOL(unregister_netdev);
#ifdef CONFIG_TR
void tr_setup(struct net_device *dev)
{
/*
* Configure and register
*/
dev->hard_header = tr_header;
dev->rebuild_header = tr_rebuild_header;
dev->type = ARPHRD_IEEE802_TR;
dev->hard_header_len = TR_HLEN;
dev->mtu = 2000;
dev->addr_len = TR_ALEN;
dev->tx_queue_len = 100; /* Long queues on tr */
memset(dev->broadcast,0xFF, TR_ALEN);
/* New-style flags. */
dev->flags = IFF_BROADCAST | IFF_MULTICAST ;
}
/**
* init_trdev - Register token ring device
* @dev: A token ring device structure to be filled in, or %NULL if a new
* struct should be allocated.
* @sizeof_priv: Size of additional driver-private structure to be allocated
* for this ethernet device
*
* Fill in the fields of the device structure with token ring-generic values.
*
* If no device structure is passed, a new one is constructed, complete with
* a private data area of size @sizeof_priv. A 32-byte (not bit)
* alignment is enforced for this private data area.
*
* If an empty string area is passed as dev->name, or a new structure is made,
* a new name string is constructed.
*/
struct net_device *init_trdev(struct net_device *dev, int sizeof_priv)
{
return init_netdev(dev, sizeof_priv, "tr%d", tr_setup);
}
/**
* alloc_trdev - Register token ring device
* @sizeof_priv: Size of additional driver-private structure to be allocated
* for this token ring device
*
* Fill in the fields of the device structure with token ring-generic values.
*
* Constructs a new net device, complete with a private data area of
* size @sizeof_priv. A 32-byte (not bit) alignment is enforced for
* this private data area.
*/
struct net_device *alloc_trdev(int sizeof_priv)
{
return alloc_netdev(sizeof_priv, "tr%d", tr_setup);
}
int register_trdev(struct net_device *dev)
{
return __register_netdev(dev);
}
void unregister_trdev(struct net_device *dev)
{
unregister_netdev(dev);
}
EXPORT_SYMBOL(tr_setup);
EXPORT_SYMBOL(init_trdev);
EXPORT_SYMBOL(alloc_trdev);
EXPORT_SYMBOL(register_trdev);
EXPORT_SYMBOL(unregister_trdev);
#endif /* CONFIG_TR */
#ifdef CONFIG_NET_FC
void fc_setup(struct net_device *dev)
{
dev->hard_header = fc_header;
dev->rebuild_header = fc_rebuild_header;
dev->type = ARPHRD_IEEE802;
dev->hard_header_len = FC_HLEN;
dev->mtu = 2024;
dev->addr_len = FC_ALEN;
dev->tx_queue_len = 100; /* Long queues on fc */
memset(dev->broadcast,0xFF, FC_ALEN);
/* New-style flags. */
dev->flags = IFF_BROADCAST;
}
/**
* init_fcdev - Register fibre channel device
* @dev: A fibre channel device structure to be filled in, or %NULL if a new
* struct should be allocated.
* @sizeof_priv: Size of additional driver-private structure to be allocated
* for this ethernet device
*
* Fill in the fields of the device structure with fibre channel-generic values.
*
* If no device structure is passed, a new one is constructed, complete with
* a private data area of size @sizeof_priv. A 32-byte (not bit)
* alignment is enforced for this private data area.
*
* If an empty string area is passed as dev->name, or a new structure is made,
* a new name string is constructed.
*/
struct net_device *init_fcdev(struct net_device *dev, int sizeof_priv)
{
return init_netdev(dev, sizeof_priv, "fc%d", fc_setup);
}
/**
* alloc_fcdev - Register fibre channel device
* @sizeof_priv: Size of additional driver-private structure to be allocated
* for this fibre channel device
*
* Fill in the fields of the device structure with fibre channel-generic values.
*
* Constructs a new net device, complete with a private data area of
* size @sizeof_priv. A 32-byte (not bit) alignment is enforced for
* this private data area.
*/
struct net_device *alloc_fcdev(int sizeof_priv)
{
return alloc_netdev(sizeof_priv, "fc%d", fc_setup);
}
int register_fcdev(struct net_device *dev)
{
return __register_netdev(dev);
}
void unregister_fcdev(struct net_device *dev)
{
unregister_netdev(dev);
}
EXPORT_SYMBOL(fc_setup);
EXPORT_SYMBOL(init_fcdev);
EXPORT_SYMBOL(alloc_fcdev);
EXPORT_SYMBOL(register_fcdev);
EXPORT_SYMBOL(unregister_fcdev);
#endif /* CONFIG_NET_FC */
File diff suppressed because it is too large. Load diff
+173
View File
@@ -0,0 +1,173 @@
/*
* New style setup code for the network devices
*/
#include <linux/config.h>
#include <linux/netdevice.h>
#include <linux/errno.h>
#include <linux/init.h>
//#include <linux/netlink.h>
extern int slip_init_ctrl_dev(void);
extern int x25_asy_init_ctrl_dev(void);
extern int dmascc_init(void);
extern int awc4500_pci_probe(void);
extern int awc4500_isa_probe(void);
extern int awc4500_pnp_probe(void);
extern int awc4500_365_probe(void);
extern int arcnet_init(void);
extern int scc_enet_init(void);
extern int fec_enet_init(void);
extern int dlci_setup(void);
extern int sdla_setup(void);
extern int sdla_c_setup(void);
extern int comx_init(void);
extern int lmc_setup(void);
extern int madgemc_probe(void);
extern int uml_net_probe(void);
/* Pad device name to IFNAMSIZ=16. F.e. __PAD6 is string of 9 zeros. */
#define __PAD6 "\0\0\0\0\0\0\0\0\0"
#define __PAD5 __PAD6 "\0"
#define __PAD4 __PAD5 "\0"
#define __PAD3 __PAD4 "\0"
#define __PAD2 __PAD3 "\0"
/*
* Devices in this list must do new style probing. That is they must
* allocate their own device objects and do their own bus scans.
*/
struct net_probe
{
int (*probe)(void);
int status; /* non-zero if autoprobe has failed */
};
static struct net_probe pci_probes[] __initdata = {
/*
* Early setup devices
*/
#if defined(CONFIG_DMASCC)
{dmascc_init, 0},
#endif
#if defined(CONFIG_DLCI)
{dlci_setup, 0},
#endif
#if defined(CONFIG_SDLA)
{sdla_c_setup, 0},
#endif
#if defined(CONFIG_ARCNET)
{arcnet_init, 0},
#endif
#if defined(CONFIG_SCC_ENET)
{scc_enet_init, 0},
#endif
#if defined(CONFIG_FEC_ENET)
{fec_enet_init, 0},
#endif
#if defined(CONFIG_COMX)
{comx_init, 0},
#endif
#if defined(CONFIG_LANMEDIA)
{lmc_setup, 0},
#endif
/*
*
* Wireless non-HAM
*
*/
#ifdef CONFIG_AIRONET4500_NONCS
#ifdef CONFIG_AIRONET4500_PCI
{awc4500_pci_probe,0},
#endif
#ifdef CONFIG_AIRONET4500_PNP
{awc4500_pnp_probe,0},
#endif
#endif
/*
* Token Ring Drivers
*/
#ifdef CONFIG_MADGEMC
{madgemc_probe, 0},
#endif
#ifdef CONFIG_UML_NET
{uml_net_probe, 0},
#endif
{NULL, 0},
};
/*
* Run the updated device probes. These do not need a device passed
* into them.
*/
static void __init network_probe(void)
{
struct net_probe *p = pci_probes;
while (p->probe != NULL)
{
p->status = p->probe();
p++;
}
}
/*
* Initialise the line discipline drivers
*/
static void __init network_ldisc_init(void)
{
#if defined(CONFIG_SLIP)
slip_init_ctrl_dev();
#endif
#if defined(CONFIG_X25_ASY)
x25_asy_init_ctrl_dev();
#endif
}
static void __init special_device_init(void)
{
#ifdef CONFIG_NET_SB1000
{
extern int sb1000_probe(struct net_device *dev);
static struct net_device sb1000_dev =
{
"cm0" __PAD3, 0x0, 0x0, 0x0, 0x0, 0, 0, 0, 0, 0, NULL, sb1000_probe
};
register_netdev(&sb1000_dev);
}
#endif
}
/*
* Initialise network devices
*/
void __init net_device_init(void)
{
/* Devices supporting the new probing API */
network_probe();
/* Line disciplines */
network_ldisc_init();
/* Special devices */
special_device_init();
/* That kicks off the legacy init functions */
}
+58
View File
@@ -0,0 +1,58 @@
21142.o: 21142.c \
tulip.h \
/home/kaf24/xeno/build/linux-2.4.16-kdb-orig/include/linux/pci.h \
/home/kaf24/xeno/build/linux-2.4.16-kdb-orig/include/linux/delay.h
eeprom.o: eeprom.c \
tulip.h \
/home/kaf24/xeno/build/linux-2.4.16-kdb-orig/include/linux/init.h \
/home/kaf24/xeno/build/linux-2.4.16-kdb-orig/include/asm/unaligned.h
interrupt.o: interrupt.c \
tulip.h \
/home/kaf24/xeno/build/linux-2.4.16-kdb-orig/include/linux/etherdevice.h \
/home/kaf24/xeno/build/linux-2.4.16-kdb-orig/include/linux/pci.h \
$(wildcard /home/kaf24/xeno/build/linux-2.4.16-kdb-orig/include/config/net/hw/flowcontrol.h)
media.o: media.c \
/home/kaf24/xeno/build/linux-2.4.16-kdb-orig/include/linux/kernel.h \
/home/kaf24/xeno/build/linux-2.4.16-kdb-orig/include/linux/mii.h \
/home/kaf24/xeno/build/linux-2.4.16-kdb-orig/include/linux/init.h \
/home/kaf24/xeno/build/linux-2.4.16-kdb-orig/include/linux/delay.h \
tulip.h
pnic2.o: pnic2.c \
tulip.h \
/home/kaf24/xeno/build/linux-2.4.16-kdb-orig/include/linux/pci.h \
/home/kaf24/xeno/build/linux-2.4.16-kdb-orig/include/linux/delay.h
pnic.o: pnic.c \
/home/kaf24/xeno/build/linux-2.4.16-kdb-orig/include/linux/kernel.h \
tulip.h
timer.o: timer.c \
tulip.h
tulip_core.o: tulip_core.c \
/home/kaf24/xeno/build/linux-2.4.16-kdb-orig/include/linux/module.h \
tulip.h \
/home/kaf24/xeno/build/linux-2.4.16-kdb-orig/include/linux/pci.h \
/home/kaf24/xeno/build/linux-2.4.16-kdb-orig/include/linux/init.h \
/home/kaf24/xeno/build/linux-2.4.16-kdb-orig/include/linux/etherdevice.h \
/home/kaf24/xeno/build/linux-2.4.16-kdb-orig/include/linux/delay.h \
/home/kaf24/xeno/build/linux-2.4.16-kdb-orig/include/linux/mii.h \
/home/kaf24/xeno/build/linux-2.4.16-kdb-orig/include/linux/ethtool.h \
/home/kaf24/xeno/build/linux-2.4.16-kdb-orig/include/asm/unaligned.h \
/home/kaf24/xeno/build/linux-2.4.16-kdb-orig/include/asm/uaccess.h \
$(wildcard /home/kaf24/xeno/build/linux-2.4.16-kdb-orig/include/config/net/hw/flowcontrol.h) \
$(wildcard /home/kaf24/xeno/build/linux-2.4.16-kdb-orig/include/config/tulip/mwi.h) \
$(wildcard /home/kaf24/xeno/build/linux-2.4.16-kdb-orig/include/config/ddb5476.h) \
$(wildcard /home/kaf24/xeno/build/linux-2.4.16-kdb-orig/include/config/ddb5477.h) \
$(wildcard /home/kaf24/xeno/build/linux-2.4.16-kdb-orig/include/config/pm.h)
tulip.h: \
/home/kaf24/xeno/build/linux-2.4.16-kdb-orig/include/linux/kernel.h \
/home/kaf24/xeno/build/linux-2.4.16-kdb-orig/include/linux/types.h \
/home/kaf24/xeno/build/linux-2.4.16-kdb-orig/include/linux/spinlock.h \
/home/kaf24/xeno/build/linux-2.4.16-kdb-orig/include/linux/netdevice.h \
/home/kaf24/xeno/build/linux-2.4.16-kdb-orig/include/linux/timer.h \
/home/kaf24/xeno/build/linux-2.4.16-kdb-orig/include/linux/delay.h \
/home/kaf24/xeno/build/linux-2.4.16-kdb-orig/include/asm/io.h \
/home/kaf24/xeno/build/linux-2.4.16-kdb-orig/include/asm/irq.h \
$(wildcard /home/kaf24/xeno/build/linux-2.4.16-kdb-orig/include/config/tulip/mmio.h) \
$(wildcard /home/kaf24/xeno/build/linux-2.4.16-kdb-orig/include/config/net/hw/flowcontrol.h)
@touch tulip.h
.PRECIOUS: tulip.h \
+239
View File
@@ -0,0 +1,239 @@
/*
drivers/net/tulip/21142.c
Maintained by Jeff Garzik <jgarzik@mandrakesoft.com>
Copyright 2000,2001 The Linux Kernel Team
Written/copyright 1994-2001 by Donald Becker.
This software may be used and distributed according to the terms
of the GNU General Public License, incorporated herein by reference.
Please refer to Documentation/DocBook/tulip.{pdf,ps,html}
for more information on this driver, or visit the project
Web page at http://sourceforge.net/projects/tulip/
*/
#include "tulip.h"
#include <linux/pci.h>
#include <linux/delay.h>
static u16 t21142_csr13[] = { 0x0001, 0x0009, 0x0009, 0x0000, 0x0001, };
u16 t21142_csr14[] = { 0xFFFF, 0x0705, 0x0705, 0x0000, 0x7F3D, };
static u16 t21142_csr15[] = { 0x0008, 0x0006, 0x000E, 0x0008, 0x0008, };
/* Handle the 21143 uniquely: do autoselect with NWay, not the EEPROM list
of available transceivers. */
void t21142_timer(unsigned long data)
{
struct net_device *dev = (struct net_device *)data;
struct tulip_private *tp = (struct tulip_private *)dev->priv;
long ioaddr = dev->base_addr;
int csr12 = inl(ioaddr + CSR12);
int next_tick = 60*HZ;
int new_csr6 = 0;
if (tulip_debug > 2)
printk(KERN_INFO"%s: 21143 negotiation status %8.8x, %s.\n",
dev->name, csr12, medianame[dev->if_port]);
if (tulip_media_cap[dev->if_port] & MediaIsMII) {
tulip_check_duplex(dev);
next_tick = 60*HZ;
} else if (tp->nwayset) {
/* Don't screw up a negotiated session! */
if (tulip_debug > 1)
printk(KERN_INFO"%s: Using NWay-set %s media, csr12 %8.8x.\n",
dev->name, medianame[dev->if_port], csr12);
} else if (tp->medialock) {
;
} else if (dev->if_port == 3) {
if (csr12 & 2) { /* No 100mbps link beat, revert to 10mbps. */
if (tulip_debug > 1)
printk(KERN_INFO"%s: No 21143 100baseTx link beat, %8.8x, "
"trying NWay.\n", dev->name, csr12);
t21142_start_nway(dev);
next_tick = 3*HZ;
}
} else if ((csr12 & 0x7000) != 0x5000) {
/* Negotiation failed. Search media types. */
if (tulip_debug > 1)
printk(KERN_INFO"%s: 21143 negotiation failed, status %8.8x.\n",
dev->name, csr12);
if (!(csr12 & 4)) { /* 10mbps link beat good. */
new_csr6 = 0x82420000;
dev->if_port = 0;
outl(0, ioaddr + CSR13);
outl(0x0003FFFF, ioaddr + CSR14);
outw(t21142_csr15[dev->if_port], ioaddr + CSR15);
outl(t21142_csr13[dev->if_port], ioaddr + CSR13);
} else {
/* Select 100mbps port to check for link beat. */
new_csr6 = 0x83860000;
dev->if_port = 3;
outl(0, ioaddr + CSR13);
outl(0x0003FF7F, ioaddr + CSR14);
outw(8, ioaddr + CSR15);
outl(1, ioaddr + CSR13);
}
if (tulip_debug > 1)
printk(KERN_INFO"%s: Testing new 21143 media %s.\n",
dev->name, medianame[dev->if_port]);
if (new_csr6 != (tp->csr6 & ~0x00D5)) {
tp->csr6 &= 0x00D5;
tp->csr6 |= new_csr6;
outl(0x0301, ioaddr + CSR12);
tulip_restart_rxtx(tp);
}
next_tick = 3*HZ;
}
/* mod_timer synchronizes us with potential add_timer calls
* from interrupts.
*/
mod_timer(&tp->timer, RUN_AT(next_tick));
}
void t21142_start_nway(struct net_device *dev)
{
struct tulip_private *tp = (struct tulip_private *)dev->priv;
long ioaddr = dev->base_addr;
int csr14 = ((tp->sym_advertise & 0x0780) << 9) |
((tp->sym_advertise & 0x0020) << 1) | 0xffbf;
dev->if_port = 0;
tp->nway = tp->mediasense = 1;
tp->nwayset = tp->lpar = 0;
if (tulip_debug > 1)
printk(KERN_DEBUG "%s: Restarting 21143 autonegotiation, csr14=%8.8x.\n",
dev->name, csr14);
outl(0x0001, ioaddr + CSR13);
udelay(100);
outl(csr14, ioaddr + CSR14);
tp->csr6 = 0x82420000 | (tp->sym_advertise & 0x0040 ? FullDuplex : 0);
outl(tp->csr6, ioaddr + CSR6);
if (tp->mtable && tp->mtable->csr15dir) {
outl(tp->mtable->csr15dir, ioaddr + CSR15);
outl(tp->mtable->csr15val, ioaddr + CSR15);
} else
outw(0x0008, ioaddr + CSR15);
outl(0x1301, ioaddr + CSR12); /* Trigger NWAY. */
}
void t21142_lnk_change(struct net_device *dev, int csr5)
{
struct tulip_private *tp = (struct tulip_private *)dev->priv;
long ioaddr = dev->base_addr;
int csr12 = inl(ioaddr + CSR12);
if (tulip_debug > 1)
printk(KERN_INFO"%s: 21143 link status interrupt %8.8x, CSR5 %x, "
"%8.8x.\n", dev->name, csr12, csr5, inl(ioaddr + CSR14));
/* If NWay finished and we have a negotiated partner capability. */
if (tp->nway && !tp->nwayset && (csr12 & 0x7000) == 0x5000) {
int setup_done = 0;
int negotiated = tp->sym_advertise & (csr12 >> 16);
tp->lpar = csr12 >> 16;
tp->nwayset = 1;
if (negotiated & 0x0100) dev->if_port = 5;
else if (negotiated & 0x0080) dev->if_port = 3;
else if (negotiated & 0x0040) dev->if_port = 4;
else if (negotiated & 0x0020) dev->if_port = 0;
else {
tp->nwayset = 0;
if ((csr12 & 2) == 0 && (tp->sym_advertise & 0x0180))
dev->if_port = 3;
}
tp->full_duplex = (tulip_media_cap[dev->if_port] & MediaAlwaysFD) ? 1:0;
if (tulip_debug > 1) {
if (tp->nwayset)
printk(KERN_INFO "%s: Switching to %s based on link "
"negotiation %4.4x & %4.4x = %4.4x.\n",
dev->name, medianame[dev->if_port], tp->sym_advertise,
tp->lpar, negotiated);
else
printk(KERN_INFO "%s: Autonegotiation failed, using %s,"
" link beat status %4.4x.\n",
dev->name, medianame[dev->if_port], csr12);
}
if (tp->mtable) {
int i;
for (i = 0; i < tp->mtable->leafcount; i++)
if (tp->mtable->mleaf[i].media == dev->if_port) {
tp->cur_index = i;
tulip_select_media(dev, 1);
setup_done = 1;
break;
}
}
if ( ! setup_done) {
tp->csr6 = (dev->if_port & 1 ? 0x838E0000 : 0x82420000) | (tp->csr6 & 0x20ff);
if (tp->full_duplex)
tp->csr6 |= 0x0200;
outl(1, ioaddr + CSR13);
}
#if 0 /* Restart shouldn't be needed. */
outl(tp->csr6 | RxOn, ioaddr + CSR6);
if (tulip_debug > 2)
printk(KERN_DEBUG "%s: Restarting Tx and Rx, CSR5 is %8.8x.\n",
dev->name, inl(ioaddr + CSR5));
#endif
tulip_start_rxtx(tp);
if (tulip_debug > 2)
printk(KERN_DEBUG "%s: Setting CSR6 %8.8x/%x CSR12 %8.8x.\n",
dev->name, tp->csr6, inl(ioaddr + CSR6),
inl(ioaddr + CSR12));
} else if ((tp->nwayset && (csr5 & 0x08000000)
&& (dev->if_port == 3 || dev->if_port == 5)
&& (csr12 & 2) == 2) ||
(tp->nway && (csr5 & (TPLnkFail)))) {
/* Link blew? Maybe restart NWay. */
del_timer_sync(&tp->timer);
t21142_start_nway(dev);
tp->timer.expires = RUN_AT(3*HZ);
add_timer(&tp->timer);
} else if (dev->if_port == 3 || dev->if_port == 5) {
if (tulip_debug > 1)
printk(KERN_INFO"%s: 21143 %s link beat %s.\n",
dev->name, medianame[dev->if_port],
(csr12 & 2) ? "failed" : "good");
if ((csr12 & 2) && ! tp->medialock) {
del_timer_sync(&tp->timer);
t21142_start_nway(dev);
tp->timer.expires = RUN_AT(3*HZ);
add_timer(&tp->timer);
} else if (dev->if_port == 5)
outl(inl(ioaddr + CSR14) & ~0x080, ioaddr + CSR14);
} else if (dev->if_port == 0 || dev->if_port == 4) {
if ((csr12 & 4) == 0)
printk(KERN_INFO"%s: 21143 10baseT link beat good.\n",
dev->name);
} else if (!(csr12 & 4)) { /* 10mbps link beat good. */
if (tulip_debug)
printk(KERN_INFO"%s: 21143 10mbps sensed media.\n",
dev->name);
dev->if_port = 0;
} else if (tp->nwayset) {
if (tulip_debug)
printk(KERN_INFO"%s: 21143 using NWay-set %s, csr6 %8.8x.\n",
dev->name, medianame[dev->if_port], tp->csr6);
} else { /* 100mbps link beat good. */
if (tulip_debug)
printk(KERN_INFO"%s: 21143 100baseTx sensed media.\n",
dev->name);
dev->if_port = 3;
tp->csr6 = 0x838E0000 | (tp->csr6 & 0x20ff);
outl(0x0003FF7F, ioaddr + CSR14);
outl(0x0301, ioaddr + CSR12);
tulip_restart_rxtx(tp);
}
}
+520
View File
@@ -0,0 +1,520 @@
2001-11-13 David S. Miller <davem@redhat.com>
* tulip_core.c (tulip_mwi_config): Kill unused label early_out.
2001-11-06 Richard Mortimer <richm@oldelvet.netscapeonline.co.uk>
* tulip_core.c: Correct set of values to mask out of csr0,
for DM9102A chips. Limit burst/alignment of DM9102A chips
on Sparcs.
2001-11-06 Jun Sun <jsun@mvista.com>
* tulip_core.c: Support finding MAC address on
two MIPS boards, DDB5476 and DDB5477.
2001-11-06 Kevin B. Hendricks <khendricks@ivey.uwo.ca>
* Makefile, tulip.h, tulip_core.c, pnic2.c, 21142.c:
Fixes for PNIC II support.
2001-11-06 David S. Miller <davem@redhat.com>
* tulip_core.c: Support reading MAC address from
Sparc OBP property local-mac-address.
2001-07-17 Erik A. Hendriks <hendriks@lanl.gov>
* 21142.c: Merge fix from tulip.c 0.92w which prevents the
overwriting of csr6 bits we want to preserve.
2001-07-10 Jeff Golds <jgolds@resilience.com>
* tulip_core.c: Fix two comments
2001-07-06 Stephen Degler <sdegler@degler.net>
* media.c:
The media selection process at the end of NWAY is busted
because for the case of MII/SYM it needs to be:
csr13 <- 0
csr14 <- 0
csr6 <- the value calculated is okay.
In the other media cases csr14 is computed by
t21142_csr14val[dev->if_port], which seems ok. The value of
zero as opposed to 3FFFFF comes straight from appendix D of the
21143 data book, and it makes logical sense because you're
bypassing all the SIA interface when you usa MII or SYM (see
figure 1-1 in the data book if your're visually oriented)
2001-07-03 Jeff Golds <jgolds@resilience.com>
* tulip_core.c (tulip_clean_tx_ring):
Clear status for in-progress Tx's, and count
Tx errors for all packets being released.
2001-06-16 Jeff Garzik <jgarzik@mandrakesoft.com>
* tulip.h, tulip_core.c:
Integrate MMIO support from devel branch, but default
it to off for stable kernel and driver series.
2001-06-16 Jeff Garzik <jgarzik@mandrakesoft.com>
* tulip_core.c (tulip_init_one):
Free descriptor rings on error.
2001-06-16 Jeff Garzik <jgarzik@mandrakesoft.com>
* tulip_core.c (tulip_mwi_config, tulip_init_one):
Large update to csr0 bus configuration code. This is not stable
yet, so it is only conditionally enabled, via CONFIG_TULIP_MWI.
2001-06-16 Jeff Garzik <jgarzik@mandrakesoft.com>
* tulip_core.c:
Initialize timer in tulip_init_one and tulip_down,
not in tulip_up.
2001-06-14 Jeff Garzik <jgarzik@mandrakesoft.com>
* tulip_core.c:
- Update tulip_suspend, tulip_resume for new PCI PM API.
- Surround suspend/resume code with CONFIG_PM.
2001-06-12 Jeff Golds <jgolds@resilience.com>
* tulip_core.c:
- Reset sw ring ptrs in tulip_up. Fixes PM resume case.
- Clean rx and tx rings on device down.
2001-06-05 David Miller <davem@redhat.com>
* tulip_core (set_rx_mode): Do not use set_bit
on an integer variable. Also fix endianness issue.
2001-06-04 Jeff Garzik <jgarzik@mandrakesoft.com>
* interrupt.c:
Simplify rx processing when CONFIG_NET_HW_FLOWCONTROL is
active, and in the process fix a bug where flow control
and low load caused rx not to be acknowledged properly.
2001-06-01 Jeff Garzik <jgarzik@mandrakesoft.com>
* tulip.h:
- Remove tulip_outl_csr helper, redundant.
- Add tulip_start_rxtx inline helper.
- tulip_stop_rxtx helper: Add synchronization. Always use current
csr6 value, instead of tp->csr6 value or value passed as arg.
- tulip_restart_rxtx helper: Add synchronization. Always
use tp->csr6 for desired mode, not value passed as arg.
- New RxOn, TxOn, RxTx constants for csr6 modes.
- Remove now-redundant constants csr6_st, csr6_sr.
* 21142.c, interrupt.c, media.c, pnic.c, tulip_core.c:
Update for above rxtx helper changes.
* interrupt.c:
- whitespace cleanup around #ifdef CONFIG_NET_HW_FLOWCONTROL,
convert tabs to spaces.
- Move tp->stats.rx_missed_errors update outside the ifdef.
2001-05-18 Jeff Garzik <jgarzik@mandrakesoft.com>
* tulip_core.c: Added ethtool support.
ETHTOOL_GDRVINFO ioctl only, for now.
2001-05-14 Robert Olsson <Robert.Olsson@data.slu.se>
* Restored HW_FLOWCONTROL from Linux 2.1 series tulip (ANK)
plus Jamal's NETIF_RX_* feedback control.
2001-05-14 Robert Olsson <Robert.Olsson@data.slu.se>
* Added support for 21143's Interrupt Mitigation.
Jamal original instigator.
2001-05-14 Robert Olsson <Robert.Olsson@data.slu.se>
* tulip_refill_rx prototype added to tulip.h
2001-05-13 Jeff Garzik <jgarzik@mandrakesoft.com>
* tulip_core.c: Remove HAS_PCI_MWI flag from Comet, untested.
2001-05-12 Jeff Garzik <jgarzik@mandrakesoft.com>
* tulip_core.c, tulip.h: Remove Conexant PCI id, no chip
docs are available to fix problems with support.
2001-05-12 Jeff Garzik <jgarzik@mandrakesoft.com>
* tulip_core.c (tulip_init_one): Do not call
unregister_netdev in error cleanup. Remnant of old
usage of init_etherdev.
2001-05-12 Jeff Garzik <jgarzik@mandrakesoft.com>
* media.c (tulip_find_mii): Simple write the updated BMCR
twice, as it seems the best thing to do for both broken and
sane chips.
If the mii_advert value, as read from MII_ADVERTISE, is zero,
then generate a value we should advertise from the capability
bits in BMSR.
Fill in tp->advertising for all cases.
Just to be safe, clear all unwanted bits.
2001-05-12 Jeff Garzik <jgarzik@mandrakesoft.com>
* tulip_core.c (private_ioctl): Fill in tp->advertising
when advertising value is changed by the user.
2001-05-12 Jeff Garzik <jgarzik@mandrakesoft.com>
* tulip_core.c: Mark Comet chips as needed the updated MWI
csr0 configuration.
2001-05-12 Jeff Garzik <jgarzik@mandrakesoft.com>
* media.c, tulip_core.c: Move MII scan into
from inlined inside tulip_init_one to new function
tulip_find_mii in media.c.
2001-05-12 Jeff Garzik <jgarzik@mandrakesoft.com>
* media.c (tulip_check_duplex):
Only restart Rx/Tx engines if they are active
(and csr6 changes)
2001-05-12 Jeff Garzik <jgarzik@mandrakesoft.com>
* tulip_core.c (tulip_mwi_config):
Clamp values read from PCI cache line size register to
values acceptable to tulip chip. Done for safety and
-almost- certainly unneeded.
2001-05-11 Jeff Garzik <jgarzik@mandrakesoft.com>
* tulip_core.c (tulip_init_one):
Instead of unconditionally enabling autonegotiation, disable
autonegotiation if not using the default port. Further,
flip the nway bit immediately, and then update the
speed/duplex in a separate MII transaction. We do this
because some boards require that nway be disabled separately,
before media selection is forced.
TODO: Investigate if we can simply write the same value
to BMCR twice, to avoid setting unnecessarily changing
phy settings.
2001-05-11 Jeff Garzik <jgarzik@mandrakesoft.com>
* tulip.h, tulip_core.c: If HAS_PCI_MWI is set for a
given chip, adjust the csr0 values not according to
provided values but according to system cache line size.
Currently cache alignment is matched as closely to cache
line size as possible. Currently programmable burst limit
is set (ie. never unlimited), and always equal to cache
alignment and system cache size. Currently MWI bit is set
only if the MWI bit is present in the PCI command register.
2001-05-11 Jeff Garzik <jgarzik@mandrakesoft.com>
* media.c (tulip_select_media):
For media types 1 and 3, only use the provided eeprom
advertising value if it is non-zero.
(tulip_check_duplex):
Do not exit ASAP if full_duplex_lock is set. This
ensures that the csr6 value is written if an update
is needed.
2001-05-10 Jeff Garzik <jgarzik@mandrakesoft.com>
Merge PNIC-II-specific stuff from Becker's tulip.c:
* tulip.h, 21142.c (pnic2_lnk_change): new function
* tulip_core.c (tulip_init_one): use it
* tulip_core.c (tulip_tx_timeout): Add specific
debugging for PNIC2.
2001-05-10 Jeff Garzik <jgarzik@mandrakesoft.com>
* tulip_core.c (tulip_init_one): Print out
tulip%d instead of PCI device number, for
consistency.
2001-05-10 Jeff Garzik <jgarzik@mandrakesoft.com>
* Merge changes from Becker's tulip.c:
Fix bugs in ioctl.
Fix several bugs by distinguishing between MII
and SYM advertising values.
Set CSR14 autonegotiation bit for media types 2 and 4,
where the SIA CSR setup values are not provided.
2001-05-10 Jeff Garzik <jgarzik@mandrakesoft.com>
* media.c (tulip_select_media): Only update MII
advertising value if startup arg < 2.
* tulip.h: Do not enable CSR13/14/15 autoconfiguration
for 21041.
* tulip_core.c:
21041: add specific code for reset, and do not set CAC bit
When resetting media, for media table type 11 media, pass
value 2 as 'startup' arg to select_media, to avoid updating
MII advertising value.
2001-05-10 Jeff Garzik <jgarzik@mandrakesoft.com>
* pnic.c (pnic_check_duplex): remove
pnic.c (pnic_lnk_change, pnic_timer): use
tulip_check_duplex not pnic_check_duplex.
* media.c (tulip_check_duplex):
Clean up to use symbolic names instead of numeric constants.
Set TxThreshold mode as necessary as well as clearing it.
Update csr6 if csr6 changes, not simply if duplex changes.
(found by Manfred Spraul)
2001-05-10 Jeff Garzik <jgarzik@mandrakesoft.com>
* 21142.c, eeprom.c, tulip.h, tulip_core.c:
Remove DPRINTK as another, better method of
debug message printing is available.
2001-05-09 Jeff Garzik <jgarzik@mandrakesoft.com>
* 21142.c (t21142_lnk_change): Pass arg startup==1
to tulip_select_media, in order to force csr13 to be
zeroed out prior to going to full duplex mode. Fixes
autonegotiation on a quad-port Znyx card.
(from Stephen Dengler)
2001-05-09 Russell King <rmk@arm.linux.org.uk>
* interrupt.c: Better PCI bus error reporting.
2001-04-03 Jeff Garzik <jgarzik@mandrakesoft.com>
* tulip_core.c: Now that dev->name is only available late
in the probe, insert a hack to replace a not-evaluated
"eth%d" string with an evaluated "tulip%d" string.
Also, remove obvious comment and an indentation cleanup.
2001-04-03 Jeff Garzik <jgarzik@mandrakesoft.com>
* tulip_core.c: If we are a module, always print out the
version string. If we are built into the kernel, only print
the version string if at least one tulip is detected.
2001-04-03 Jeff Garzik <jgarzik@mandrakesoft.com>
Merged from Becker's tulip.c 0.92t:
* tulip_core.c: Add support for Conexant LANfinity.
2001-04-03 Jeff Garzik <jgarzik@mandrakesoft.com>
* tulip_core.c: Only suspend/resume if the interface
is up and running. Use alloc_etherdev and pci_request_regions.
Spelling fix.
2001-04-03 Jeff Garzik <jgarzik@mandrakesoft.com>
* tulip_core.c: Remove code that existed when one or more of
the following defines existed. These defines were never used
by normal users in practice: TULIP_FULL_DUPLEX,
TULIP_DEFAULT_MEDIA, and TULIP_NO_MEDIA_SWITCH.
* tulip.h, eeprom.c: Move EE_* constants from tulip.h to eeprom.c.
* tulip.h, media.c: Move MDIO_* constants from tulip.h to media.c.
* media.c: Add barrier() to mdio_read/write's PNIC status check
loops.
2001-04-03 Jeff Garzik <jgarzik@mandrakesoft.com>
Merged from Becker's tulip.c 0.92t:
* tulip.h: Add MEDIA_MASK constant for bounding medianame[]
array lookups.
* eeprom.c, media.c, timer.c, tulip_core.c: Use it.
* media.c, tulip_core.c: mdio_{read,write} cleanup. Since this
is called [pretty much] directly from ioctl, we mask
read/write arguments to limit the values passed.
Added mii_lock. Added comet_miireg2offset and better
Comet-specific mdio_read/write code. Pay closer attention
to the bits we set in ioctl. Remove spinlocks from ioctl,
they are in mdio_read/write now. Use mask to limit
phy number in tulip_init_one's MII scan.
2001-04-03 Jeff Garzik <jgarzik@mandrakesoft.com>
Merged from Becker's tulip.c 0.92t:
* 21142.c, tulip_core.c: PNIC2 MAC address and NWay fixes.
* tulip.h: Add FullDuplex constant, used in above change.
2001-04-03 Jeff Garzik <jgarzik@mandrakesoft.com>
* timer.c: Do not call netif_carrier_{on,off}, it is not used in
the main tree. Leave code in, disabled, as markers for future
carrier notification.
2001-04-03 Jeff Garzik <jgarzik@mandrakesoft.com>
Merged from Becker's tulip.c 0.92t, except for the tulip.h
whitespace cleanup:
* interrupt.c: If Rx stops, make sure to update the
multicast filter before restarting.
* tulip.h: Add COMET_MAC_ADDR feature flag, clean up flags.
Add Accept* Rx mode bit constants.
Add mc_filter[] to driver private struct.
* tulip_core.c: Add new Comet PCI id 0x1113:0x9511.
Add COMET_MAC_ADDR feature flag to comet entry in board info array.
Prefer to test COMET_MAC_ADDR flag to testing chip_id for COMET,
when dealing with the Comet's MAC address.
Enable Tx underrun recovery for Comet chips.
Use new Accept* constants in set_rx_mode.
Prefer COMET_MAC_ADDR flag test to chip_id test in set_rx_mode.
Store built mc_filter for later use in intr handler by Comets.
2001-04-03 Jeff Garzik <jgarzik@mandrakesoft.com>
* tulip_core.c: Use tp->cur_tx when building the
setup frame, instead of assuming that the setup
frame is always built in slot zero. This case is
hit during PM resume.
2001-04-03 Jeff Garzik <jgarzik@mandrakesoft.com>
* *.c: Update file headers (copyright, urls, etc.)
* Makefile: re-order to that chip-specific modules on own line
* eeprom.c: BSS/zero-init cleanup (Andrey Panin)
* tulip_core.c: merge medianame[] update from tulip.c.
Additional arch-specific rx_copybreak, csr0 values. (various)
2001-02-20 Jeff Garzik <jgarzik@mandrakesoft.com>
* media.c (tulip_select_media): No need to initialize
new_csr6, all cases initialize it properly.
2001-02-18 Manfred Spraul <manfred@colorfullife.com>
* interrupt.c (tulip_refill_rx): Make public.
If PNIC chip stops due to lack of Rx buffers, restart it.
(tulip_interrupt): PNIC doesn't have a h/w timer, emulate
with software timers.
* pnic.c (pnic_check_duplex): New function, PNIC-specific
version of tulip_check_duplex.
(pnic_lnk_change): Call pnic_check_duplex. If we use an
external MII, then we mustn't use the internal negotiation.
(pnic_timer): Support Rx refilling on work overflow in
interrupt handler, as PNIC doesn't support a h/w timer.
* tulip_core.c (tulip_tbl[]): Modify default csr6
2001-02-11 Jeff Garzik <jgarzik@mandrakesoft.com>
* tulip_core.c (tulip_init_one): Call pci_enable_device
to ensure wakeup/resource assignment before checking those
values.
(tulip_init_one): Replace PCI ids with constants from pci_id.h.
(tulip_suspend, tulip_resume, tulip_remove_one): Call
pci_power_on/off (commented out for now).
2001-02-10 Jeff Garzik <jgarzik@mandrakesoft.com>
* tulip.h: Add CFDD_xxx bits for Tulip power management
* tulip_core.c (tulip_set_power_state): New function,
manipulating Tulip chip power state where supported.
(tulip_up, tulip_down, tulip_init_one): Use it.
2001-02-10 Jeff Garzik <jgarzik@mandrakesoft.com>
* tulip_core.c (tulip_tx_timeout): Call netif_wake_queue
to ensure the next Tx is always sent to us.
2001-01-27 Jeff Garzik <jgarzik@mandrakesoft.com>
* tulip_core.c (tulip_remove_one): Fix mem leak by freeing
tp->media_tbl. Add check for !dev, reformat code appropriately.
2001-01-27 Jeff Garzik <jgarzik@mandrakesoft.com>
* tulip_tbl[]: Comment all entries to make order and chip_id
relationship more clear.
* tulip_pci_tbl[]: Add new Accton PCI id (COMET chipset).
2001-01-16 Jeff Garzik <jgarzik@mandrakesoft.com>
* tulip_core.c: static vars no longer explicitly
initialized to zero.
* eeprom.c (tulip_read_eeprom): Make sure to delay between
EE_ENB and EE_ENB|EE_SHIFT_CLK. Merged from becker tulip.c.
2001-01-05 Peter De Schrijver <p2@mind.be>
* eeprom.c (tulip_parse_eeprom): Interpret a bit more of 21142
extended format type 3 info blocks in a tulip SROM.
2001-01-03 Matti Aarnio <matti.aarnio@zmailer.org>
* media.c (tulip_select_media): Support media types 5 and 6
2001-??-?? ??
* tulip_core.c: Add comment about LanMedia needing
a different driver.
Enable workarounds for early PCI chipsets.
Add IA64 csr0 support, update HPPA csr0 support.
2000-12-17 Alan Cox <alan@redhat.com>
* eeprom.c, timer.c, tulip.h, tulip_core.c: Merge support
for the Davicom's quirks into the main tulip.
Patch by Tobias Ringstrom
2000-11-08 Jim Studt <jim@federated.com>
* eeprom.c (tulip_parse_eeprom): Check array bounds for
medianame[] and block_name[] arrays to avoid oops due
to bad values returned from hardware.
2000-11-02 Jeff Garzik <jgarzik@mandrakesoft.com>
* tulip_core.c (set_rx_mode): This is synchronized via
dev->xmit_lock, so only the queueing of the setup frame needs to
be locked, against tulip_interrupt.
2000-11-02 Alexey Kuznetov <kuznet@ms2.inr.ac.ru>
* timer.c (tulip_timer): Call netif_carrier_{on,off} to report
link state to the rest of the kernel, and userspace.
* interrupt.c (tulip_interrupt): Remove tx_full.
* tulip.h: Likewise.
* tulip_core.c (tulip_init_ring, tulip_start_xmit, set_rx_mode):
Likewise.
2000-10-18 Jeff Garzik <jgarzik@mandrakesoft.com>
* tulip_core.c: (tulip_init_one) Print out ethernet interface
on error. Print out a message when pci_enable_device fails.
Handle DMA alloc failure.
2000-10-18 Jeff Garzik <jgarzik@mandrakesoft.com>
* Makefile: New file.
* tulip_core.c (tulip_init_one): Correct error messages
on PIO/MMIO region reserve failure.
(tulip_init_one) Add new check to ensure that PIO region is
sufficient for our needs.
+8
View File
@@ -0,0 +1,8 @@
include $(BASEDIR)/Rules.mk
default: $(OBJS)
$(LD) -r -o tulip.o $(OBJS)
clean:
rm -f *.o *~ core
+320
View File
@@ -0,0 +1,320 @@
/*
drivers/net/tulip/eeprom.c
Maintained by Jeff Garzik <jgarzik@mandrakesoft.com>
Copyright 2000,2001 The Linux Kernel Team
Written/copyright 1994-2001 by Donald Becker.
This software may be used and distributed according to the terms
of the GNU General Public License, incorporated herein by reference.
Please refer to Documentation/DocBook/tulip.{pdf,ps,html}
for more information on this driver, or visit the project
Web page at http://sourceforge.net/projects/tulip/
*/
#include "tulip.h"
#include <linux/init.h>
#include <asm/unaligned.h>
/* Serial EEPROM section. */
/* The main routine to parse the very complicated SROM structure.
Search www.digital.com for "21X4 SROM" to get details.
This code is very complex, and will require changes to support
additional cards, so I'll be verbose about what is going on.
*/
/* Known cards that have old-style EEPROMs. */
static struct eeprom_fixup eeprom_fixups[] __devinitdata = {
{"Asante", 0, 0, 0x94, {0x1e00, 0x0000, 0x0800, 0x0100, 0x018c,
0x0000, 0x0000, 0xe078, 0x0001, 0x0050, 0x0018 }},
{"SMC9332DST", 0, 0, 0xC0, { 0x1e00, 0x0000, 0x0800, 0x041f,
0x0000, 0x009E, /* 10baseT */
0x0004, 0x009E, /* 10baseT-FD */
0x0903, 0x006D, /* 100baseTx */
0x0905, 0x006D, /* 100baseTx-FD */ }},
{"Cogent EM100", 0, 0, 0x92, { 0x1e00, 0x0000, 0x0800, 0x063f,
0x0107, 0x8021, /* 100baseFx */
0x0108, 0x8021, /* 100baseFx-FD */
0x0100, 0x009E, /* 10baseT */
0x0104, 0x009E, /* 10baseT-FD */
0x0103, 0x006D, /* 100baseTx */
0x0105, 0x006D, /* 100baseTx-FD */ }},
{"Maxtech NX-110", 0, 0, 0xE8, { 0x1e00, 0x0000, 0x0800, 0x0513,
0x1001, 0x009E, /* 10base2, CSR12 0x10*/
0x0000, 0x009E, /* 10baseT */
0x0004, 0x009E, /* 10baseT-FD */
0x0303, 0x006D, /* 100baseTx, CSR12 0x03 */
0x0305, 0x006D, /* 100baseTx-FD CSR12 0x03 */}},
{"Accton EN1207", 0, 0, 0xE8, { 0x1e00, 0x0000, 0x0800, 0x051F,
0x1B01, 0x0000, /* 10base2, CSR12 0x1B */
0x0B00, 0x009E, /* 10baseT, CSR12 0x0B */
0x0B04, 0x009E, /* 10baseT-FD,CSR12 0x0B */
0x1B03, 0x006D, /* 100baseTx, CSR12 0x1B */
0x1B05, 0x006D, /* 100baseTx-FD CSR12 0x1B */
}},
{"NetWinder", 0x00, 0x10, 0x57,
/* Default media = MII
* MII block, reset sequence (3) = 0x0821 0x0000 0x0001, capabilities 0x01e1
*/
{ 0x1e00, 0x0000, 0x000b, 0x8f01, 0x0103, 0x0300, 0x0821, 0x000, 0x0001, 0x0000, 0x01e1 }
},
{0, 0, 0, 0, {}}};
static const char *block_name[] __devinitdata = {
"21140 non-MII",
"21140 MII PHY",
"21142 Serial PHY",
"21142 MII PHY",
"21143 SYM PHY",
"21143 reset method"
};
void __devinit tulip_parse_eeprom(struct net_device *dev)
{
/* The last media info list parsed, for multiport boards. */
static struct mediatable *last_mediatable;
static unsigned char *last_ee_data;
static int controller_index;
struct tulip_private *tp = (struct tulip_private *)dev->priv;
unsigned char *ee_data = tp->eeprom;
int i;
tp->mtable = 0;
/* Detect an old-style (SA only) EEPROM layout:
memcmp(eedata, eedata+16, 8). */
for (i = 0; i < 8; i ++)
if (ee_data[i] != ee_data[16+i])
break;
if (i >= 8) {
if (ee_data[0] == 0xff) {
if (last_mediatable) {
controller_index++;
printk(KERN_INFO "%s: Controller %d of multiport board.\n",
dev->name, controller_index);
tp->mtable = last_mediatable;
ee_data = last_ee_data;
goto subsequent_board;
} else
printk(KERN_INFO "%s: Missing EEPROM, this interface may "
"not work correctly!\n",
dev->name);
return;
}
/* Do a fix-up based on the vendor half of the station address prefix. */
for (i = 0; eeprom_fixups[i].name; i++) {
if (dev->dev_addr[0] == eeprom_fixups[i].addr0
&& dev->dev_addr[1] == eeprom_fixups[i].addr1
&& dev->dev_addr[2] == eeprom_fixups[i].addr2) {
if (dev->dev_addr[2] == 0xE8 && ee_data[0x1a] == 0x55)
i++; /* An Accton EN1207, not an outlaw Maxtech. */
memcpy(ee_data + 26, eeprom_fixups[i].newtable,
sizeof(eeprom_fixups[i].newtable));
printk(KERN_INFO "%s: Old format EEPROM on '%s' board. Using"
" substitute media control info.\n",
dev->name, eeprom_fixups[i].name);
break;
}
}
if (eeprom_fixups[i].name == NULL) { /* No fixup found. */
printk(KERN_INFO "%s: Old style EEPROM with no media selection "
"information.\n",
dev->name);
return;
}
}
controller_index = 0;
if (ee_data[19] > 1) { /* Multiport board. */
last_ee_data = ee_data;
}
subsequent_board:
if (ee_data[27] == 0) { /* No valid media table. */
} else if (tp->chip_id == DC21041) {
unsigned char *p = (void *)ee_data + ee_data[27 + controller_index*3];
int media = get_u16(p);
int count = p[2];
p += 3;
printk(KERN_INFO "%s: 21041 Media table, default media %4.4x (%s).\n",
dev->name, media,
media & 0x0800 ? "Autosense" : medianame[media & MEDIA_MASK]);
for (i = 0; i < count; i++) {
unsigned char media_block = *p++;
int media_code = media_block & MEDIA_MASK;
if (media_block & 0x40)
p += 6;
printk(KERN_INFO "%s: 21041 media #%d, %s.\n",
dev->name, media_code, medianame[media_code]);
}
} else {
unsigned char *p = (void *)ee_data + ee_data[27];
unsigned char csr12dir = 0;
int count, new_advertise = 0;
struct mediatable *mtable;
u16 media = get_u16(p);
p += 2;
if (tp->flags & CSR12_IN_SROM)
csr12dir = *p++;
count = *p++;
/* there is no phy information, don't even try to build mtable */
if (count == 0) {
if (tulip_debug > 0)
printk(KERN_WARNING "%s: no phy info, aborting mtable build\n", dev->name);
return;
}
mtable = (struct mediatable *)
kmalloc(sizeof(struct mediatable) + count*sizeof(struct medialeaf),
GFP_KERNEL);
if (mtable == NULL)
return; /* Horrible, impossible failure. */
last_mediatable = tp->mtable = mtable;
mtable->defaultmedia = media;
mtable->leafcount = count;
mtable->csr12dir = csr12dir;
mtable->has_nonmii = mtable->has_mii = mtable->has_reset = 0;
mtable->csr15dir = mtable->csr15val = 0;
printk(KERN_INFO "%s: EEPROM default media type %s.\n", dev->name,
media & 0x0800 ? "Autosense" : medianame[media & MEDIA_MASK]);
for (i = 0; i < count; i++) {
struct medialeaf *leaf = &mtable->mleaf[i];
if ((p[0] & 0x80) == 0) { /* 21140 Compact block. */
leaf->type = 0;
leaf->media = p[0] & 0x3f;
leaf->leafdata = p;
if ((p[2] & 0x61) == 0x01) /* Bogus, but Znyx boards do it. */
mtable->has_mii = 1;
p += 4;
} else {
leaf->type = p[1];
if (p[1] == 0x05) {
mtable->has_reset = i;
leaf->media = p[2] & 0x0f;
} else if (tp->chip_id == DM910X && p[1] == 0x80) {
/* Hack to ignore Davicom delay period block */
mtable->leafcount--;
count--;
i--;
leaf->leafdata = p + 2;
p += (p[0] & 0x3f) + 1;
continue;
} else if (p[1] & 1) {
int gpr_len, reset_len;
mtable->has_mii = 1;
leaf->media = 11;
gpr_len=p[3]*2;
reset_len=p[4+gpr_len]*2;
new_advertise |= get_u16(&p[7+gpr_len+reset_len]);
} else {
mtable->has_nonmii = 1;
leaf->media = p[2] & MEDIA_MASK;
/* Davicom's media number for 100BaseTX is strange */
if (tp->chip_id == DM910X && leaf->media == 1)
leaf->media = 3;
switch (leaf->media) {
case 0: new_advertise |= 0x0020; break;
case 4: new_advertise |= 0x0040; break;
case 3: new_advertise |= 0x0080; break;
case 5: new_advertise |= 0x0100; break;
case 6: new_advertise |= 0x0200; break;
}
if (p[1] == 2 && leaf->media == 0) {
if (p[2] & 0x40) {
u32 base15 = get_unaligned((u16*)&p[7]);
mtable->csr15dir =
(get_unaligned((u16*)&p[9])<<16) + base15;
mtable->csr15val =
(get_unaligned((u16*)&p[11])<<16) + base15;
} else {
mtable->csr15dir = get_unaligned((u16*)&p[3])<<16;
mtable->csr15val = get_unaligned((u16*)&p[5])<<16;
}
}
}
leaf->leafdata = p + 2;
p += (p[0] & 0x3f) + 1;
}
if (tulip_debug > 1 && leaf->media == 11) {
unsigned char *bp = leaf->leafdata;
printk(KERN_INFO "%s: MII interface PHY %d, setup/reset "
"sequences %d/%d long, capabilities %2.2x %2.2x.\n",
dev->name, bp[0], bp[1], bp[2 + bp[1]*2],
bp[5 + bp[2 + bp[1]*2]*2], bp[4 + bp[2 + bp[1]*2]*2]);
}
printk(KERN_INFO "%s: Index #%d - Media %s (#%d) described "
"by a %s (%d) block.\n",
dev->name, i, medianame[leaf->media & 15], leaf->media,
leaf->type < ARRAY_SIZE(block_name) ? block_name[leaf->type] : "<unknown>",
leaf->type);
}
if (new_advertise)
tp->sym_advertise = new_advertise;
}
}
/* Reading a serial EEPROM is a "bit" grungy, but we work our way through:->.*/
/* EEPROM_Ctrl bits. */
#define EE_SHIFT_CLK 0x02 /* EEPROM shift clock. */
#define EE_CS 0x01 /* EEPROM chip select. */
#define EE_DATA_WRITE 0x04 /* Data from the Tulip to EEPROM. */
#define EE_WRITE_0 0x01
#define EE_WRITE_1 0x05
#define EE_DATA_READ 0x08 /* Data from the EEPROM chip. */
#define EE_ENB (0x4800 | EE_CS)
/* Delay between EEPROM clock transitions.
Even at 33Mhz current PCI implementations don't overrun the EEPROM clock.
We add a bus turn-around to insure that this remains true. */
#define eeprom_delay() inl(ee_addr)
/* The EEPROM commands include the alway-set leading bit. */
#define EE_READ_CMD (6)
/* Note: this routine returns extra data bits for size detection. */
int __devinit tulip_read_eeprom(long ioaddr, int location, int addr_len)
{
int i;
unsigned retval = 0;
long ee_addr = ioaddr + CSR9;
int read_cmd = location | (EE_READ_CMD << addr_len);
outl(EE_ENB & ~EE_CS, ee_addr);
outl(EE_ENB, ee_addr);
/* Shift the read command bits out. */
for (i = 4 + addr_len; i >= 0; i--) {
short dataval = (read_cmd & (1 << i)) ? EE_DATA_WRITE : 0;
outl(EE_ENB | dataval, ee_addr);
eeprom_delay();
outl(EE_ENB | dataval | EE_SHIFT_CLK, ee_addr);
eeprom_delay();
retval = (retval << 1) | ((inl(ee_addr) & EE_DATA_READ) ? 1 : 0);
}
outl(EE_ENB, ee_addr);
eeprom_delay();
for (i = 16; i > 0; i--) {
outl(EE_ENB | EE_SHIFT_CLK, ee_addr);
eeprom_delay();
retval = (retval << 1) | ((inl(ee_addr) & EE_DATA_READ) ? 1 : 0);
outl(EE_ENB, ee_addr);
eeprom_delay();
}
/* Terminate the EEPROM access. */
outl(EE_ENB & ~EE_CS, ee_addr);
return retval;
}
+559
View File
@@ -0,0 +1,559 @@
/*
drivers/net/tulip/interrupt.c
Maintained by Jeff Garzik <jgarzik@mandrakesoft.com>
Copyright 2000,2001 The Linux Kernel Team
Written/copyright 1994-2001 by Donald Becker.
This software may be used and distributed according to the terms
of the GNU General Public License, incorporated herein by reference.
Please refer to Documentation/DocBook/tulip.{pdf,ps,html}
for more information on this driver, or visit the project
Web page at http://sourceforge.net/projects/tulip/
*/
#include "tulip.h"
#include <linux/config.h>
#include <linux/etherdevice.h>
#include <linux/pci.h>
int tulip_rx_copybreak;
unsigned int tulip_max_interrupt_work;
#ifdef CONFIG_NET_HW_FLOWCONTROL
#define MIT_SIZE 15
unsigned int mit_table[MIT_SIZE+1] =
{
/* CRS11 21143 hardware Mitigation Control Interrupt
We use only RX mitigation we other techniques for
TX intr. mitigation.
31 Cycle Size (timer control)
30:27 TX timer in 16 * Cycle size
26:24 TX No pkts before Int.
23:20 RX timer in Cycle size
19:17 RX No pkts before Int.
16 Continues Mode (CM)
*/
0x0, /* IM disabled */
0x80150000, /* RX time = 1, RX pkts = 2, CM = 1 */
0x80150000,
0x80270000,
0x80370000,
0x80490000,
0x80590000,
0x80690000,
0x807B0000,
0x808B0000,
0x809D0000,
0x80AD0000,
0x80BD0000,
0x80CF0000,
0x80DF0000,
// 0x80FF0000 /* RX time = 16, RX pkts = 7, CM = 1 */
0x80F10000 /* RX time = 16, RX pkts = 0, CM = 1 */
};
#endif
int tulip_refill_rx(struct net_device *dev)
{
struct tulip_private *tp = (struct tulip_private *)dev->priv;
int entry;
int refilled = 0;
/* Refill the Rx ring buffers. */
for (; tp->cur_rx - tp->dirty_rx > 0; tp->dirty_rx++) {
entry = tp->dirty_rx % RX_RING_SIZE;
if (tp->rx_buffers[entry].skb == NULL) {
struct sk_buff *skb;
dma_addr_t mapping;
skb = tp->rx_buffers[entry].skb = dev_alloc_skb(PKT_BUF_SZ);
if (skb == NULL)
break;
mapping = pci_map_single(tp->pdev, skb->tail, PKT_BUF_SZ,
PCI_DMA_FROMDEVICE);
tp->rx_buffers[entry].mapping = mapping;
skb->dev = dev; /* Mark as being used by this device. */
tp->rx_ring[entry].buffer1 = cpu_to_le32(mapping);
refilled++;
}
tp->rx_ring[entry].status = cpu_to_le32(DescOwned);
}
if(tp->chip_id == LC82C168) {
if(((inl(dev->base_addr + CSR5)>>17)&0x07) == 4) {
/* Rx stopped due to out of buffers,
* restart it
*/
outl(0x01, dev->base_addr + CSR2);
}
}
return refilled;
}
static int tulip_rx(struct net_device *dev)
{
struct tulip_private *tp = (struct tulip_private *)dev->priv;
int entry = tp->cur_rx % RX_RING_SIZE;
int rx_work_limit = tp->dirty_rx + RX_RING_SIZE - tp->cur_rx;
int received = 0;
#ifdef CONFIG_NET_HW_FLOWCONTROL
int drop = 0, mit_sel = 0;
/* that one buffer is needed for mit activation; or might be a
bug in the ring buffer code; check later -- JHS*/
if (rx_work_limit >=RX_RING_SIZE) rx_work_limit--;
#endif
if (tulip_debug > 4)
printk(KERN_DEBUG " In tulip_rx(), entry %d %8.8x.\n", entry,
tp->rx_ring[entry].status);
/* If we own the next entry, it is a new packet. Send it up. */
while ( ! (tp->rx_ring[entry].status & cpu_to_le32(DescOwned))) {
s32 status = le32_to_cpu(tp->rx_ring[entry].status);
if (tulip_debug > 5)
printk(KERN_DEBUG "%s: In tulip_rx(), entry %d %8.8x.\n",
dev->name, entry, status);
if (--rx_work_limit < 0)
break;
if ((status & 0x38008300) != 0x0300) {
if ((status & 0x38000300) != 0x0300) {
/* Ingore earlier buffers. */
if ((status & 0xffff) != 0x7fff) {
if (tulip_debug > 1)
printk(KERN_WARNING "%s: Oversized Ethernet frame "
"spanned multiple buffers, status %8.8x!\n",
dev->name, status);
tp->stats.rx_length_errors++;
}
} else if (status & RxDescFatalErr) {
/* There was a fatal error. */
if (tulip_debug > 2)
printk(KERN_DEBUG "%s: Receive error, Rx status %8.8x.\n",
dev->name, status);
tp->stats.rx_errors++; /* end of a packet.*/
if (status & 0x0890) tp->stats.rx_length_errors++;
if (status & 0x0004) tp->stats.rx_frame_errors++;
if (status & 0x0002) tp->stats.rx_crc_errors++;
if (status & 0x0001) tp->stats.rx_fifo_errors++;
}
} else {
/* Omit the four octet CRC from the length. */
short pkt_len = ((status >> 16) & 0x7ff) - 4;
struct sk_buff *skb;
#ifndef final_version
if (pkt_len > 1518) {
printk(KERN_WARNING "%s: Bogus packet size of %d (%#x).\n",
dev->name, pkt_len, pkt_len);
pkt_len = 1518;
tp->stats.rx_length_errors++;
}
#endif
#ifdef CONFIG_NET_HW_FLOWCONTROL
drop = atomic_read(&netdev_dropping);
if (drop)
goto throttle;
#endif
/* Check if the packet is long enough to accept without copying
to a minimally-sized skbuff. */
if (pkt_len < tulip_rx_copybreak
&& (skb = dev_alloc_skb(pkt_len + 2)) != NULL) {
skb->dev = dev;
skb_reserve(skb, 2); /* 16 byte align the IP header */
pci_dma_sync_single(tp->pdev,
tp->rx_buffers[entry].mapping,
pkt_len, PCI_DMA_FROMDEVICE);
#if ! defined(__alpha__)
eth_copy_and_sum(skb, tp->rx_buffers[entry].skb->tail,
pkt_len, 0);
skb_put(skb, pkt_len);
#else
memcpy(skb_put(skb, pkt_len),
tp->rx_buffers[entry].skb->tail,
pkt_len);
#endif
} else { /* Pass up the skb already on the Rx ring. */
char *temp = skb_put(skb = tp->rx_buffers[entry].skb,
pkt_len);
#ifndef final_version
if (tp->rx_buffers[entry].mapping !=
le32_to_cpu(tp->rx_ring[entry].buffer1)) {
printk(KERN_ERR "%s: Internal fault: The skbuff addresses "
"do not match in tulip_rx: %08x vs. %08x %p / %p.\n",
dev->name,
le32_to_cpu(tp->rx_ring[entry].buffer1),
tp->rx_buffers[entry].mapping,
skb->head, temp);
}
#endif
pci_unmap_single(tp->pdev, tp->rx_buffers[entry].mapping,
PKT_BUF_SZ, PCI_DMA_FROMDEVICE);
tp->rx_buffers[entry].skb = NULL;
tp->rx_buffers[entry].mapping = 0;
}
skb->protocol = eth_type_trans(skb, dev);
#ifdef CONFIG_NET_HW_FLOWCONTROL
mit_sel =
#endif
netif_rx(skb);
#ifdef CONFIG_NET_HW_FLOWCONTROL
switch (mit_sel) {
case NET_RX_SUCCESS:
case NET_RX_CN_LOW:
case NET_RX_CN_MOD:
break;
case NET_RX_CN_HIGH:
rx_work_limit -= NET_RX_CN_HIGH; /* additional*/
break;
case NET_RX_DROP:
rx_work_limit = -1;
break;
default:
printk("unknown feedback return code %d\n", mit_sel);
break;
}
drop = atomic_read(&netdev_dropping);
if (drop) {
throttle:
rx_work_limit = -1;
mit_sel = NET_RX_DROP;
if (tp->fc_bit) {
long ioaddr = dev->base_addr;
/* disable Rx & RxNoBuf ints. */
outl(tulip_tbl[tp->chip_id].valid_intrs&RX_A_NBF_STOP, ioaddr + CSR7);
set_bit(tp->fc_bit, &netdev_fc_xoff);
}
}
#endif
dev->last_rx = jiffies;
tp->stats.rx_packets++;
tp->stats.rx_bytes += pkt_len;
}
received++;
entry = (++tp->cur_rx) % RX_RING_SIZE;
}
#ifdef CONFIG_NET_HW_FLOWCONTROL
/* We use this simplistic scheme for IM. It's proven by
real life installations. We can have IM enabled
continuesly but this would cause unnecessary latency.
Unfortunely we can't use all the NET_RX_* feedback here.
This would turn on IM for devices that is not contributing
to backlog congestion with unnecessary latency.
We monitor the the device RX-ring and have:
HW Interrupt Mitigation either ON or OFF.
ON: More then 1 pkt received (per intr.) OR we are dropping
OFF: Only 1 pkt received
Note. We only use min and max (0, 15) settings from mit_table */
if( tp->flags & HAS_INTR_MITIGATION) {
if((received > 1 || mit_sel == NET_RX_DROP)
&& tp->mit_sel != 15 ) {
tp->mit_sel = 15;
tp->mit_change = 1; /* Force IM change */
}
if((received <= 1 && mit_sel != NET_RX_DROP) && tp->mit_sel != 0 ) {
tp->mit_sel = 0;
tp->mit_change = 1; /* Force IM change */
}
}
return RX_RING_SIZE+1; /* maxrx+1 */
#else
return received;
#endif
}
/* The interrupt handler does all of the Rx thread work and cleans up
after the Tx thread. */
void tulip_interrupt(int irq, void *dev_instance, struct pt_regs *regs)
{
struct net_device *dev = (struct net_device *)dev_instance;
struct tulip_private *tp = (struct tulip_private *)dev->priv;
long ioaddr = dev->base_addr;
int csr5;
int entry;
int missed;
int rx = 0;
int tx = 0;
int oi = 0;
int maxrx = RX_RING_SIZE;
int maxtx = TX_RING_SIZE;
int maxoi = TX_RING_SIZE;
unsigned int work_count = tulip_max_interrupt_work;
/* Let's see whether the interrupt really is for us */
csr5 = inl(ioaddr + CSR5);
if ((csr5 & (NormalIntr|AbnormalIntr)) == 0)
return;
tp->nir++;
do {
/* Acknowledge all of the current interrupt sources ASAP. */
outl(csr5 & 0x0001ffff, ioaddr + CSR5);
if (tulip_debug > 4)
printk(KERN_DEBUG "%s: interrupt csr5=%#8.8x new csr5=%#8.8x.\n",
dev->name, csr5, inl(dev->base_addr + CSR5));
if (csr5 & (RxIntr | RxNoBuf)) {
#ifdef CONFIG_NET_HW_FLOWCONTROL
if ((!tp->fc_bit) ||
(!test_bit(tp->fc_bit, &netdev_fc_xoff)))
#endif
rx += tulip_rx(dev);
tulip_refill_rx(dev);
}
if (csr5 & (TxNoBuf | TxDied | TxIntr | TimerInt)) {
unsigned int dirty_tx;
spin_lock(&tp->lock);
for (dirty_tx = tp->dirty_tx; tp->cur_tx - dirty_tx > 0;
dirty_tx++) {
int entry = dirty_tx % TX_RING_SIZE;
int status = le32_to_cpu(tp->tx_ring[entry].status);
if (status < 0)
break; /* It still has not been Txed */
/* Check for Rx filter setup frames. */
if (tp->tx_buffers[entry].skb == NULL) {
/* test because dummy frames not mapped */
if (tp->tx_buffers[entry].mapping)
pci_unmap_single(tp->pdev,
tp->tx_buffers[entry].mapping,
sizeof(tp->setup_frame),
PCI_DMA_TODEVICE);
continue;
}
if (status & 0x8000) {
/* There was an major error, log it. */
#ifndef final_version
if (tulip_debug > 1)
printk(KERN_DEBUG "%s: Transmit error, Tx status %8.8x.\n",
dev->name, status);
#endif
tp->stats.tx_errors++;
if (status & 0x4104) tp->stats.tx_aborted_errors++;
if (status & 0x0C00) tp->stats.tx_carrier_errors++;
if (status & 0x0200) tp->stats.tx_window_errors++;
if (status & 0x0002) tp->stats.tx_fifo_errors++;
if ((status & 0x0080) && tp->full_duplex == 0)
tp->stats.tx_heartbeat_errors++;
} else {
tp->stats.tx_bytes +=
tp->tx_buffers[entry].skb->len;
tp->stats.collisions += (status >> 3) & 15;
tp->stats.tx_packets++;
}
pci_unmap_single(tp->pdev, tp->tx_buffers[entry].mapping,
tp->tx_buffers[entry].skb->len,
PCI_DMA_TODEVICE);
/* Free the original skb. */
dev_kfree_skb_irq(tp->tx_buffers[entry].skb);
tp->tx_buffers[entry].skb = NULL;
tp->tx_buffers[entry].mapping = 0;
tx++;
}
#ifndef final_version
if (tp->cur_tx - dirty_tx > TX_RING_SIZE) {
printk(KERN_ERR "%s: Out-of-sync dirty pointer, %d vs. %d.\n",
dev->name, dirty_tx, tp->cur_tx);
dirty_tx += TX_RING_SIZE;
}
#endif
if (tp->cur_tx - dirty_tx < TX_RING_SIZE - 2)
netif_wake_queue(dev);
tp->dirty_tx = dirty_tx;
if (csr5 & TxDied) {
if (tulip_debug > 2)
printk(KERN_WARNING "%s: The transmitter stopped."
" CSR5 is %x, CSR6 %x, new CSR6 %x.\n",
dev->name, csr5, inl(ioaddr + CSR6), tp->csr6);
tulip_restart_rxtx(tp);
}
spin_unlock(&tp->lock);
}
/* Log errors. */
if (csr5 & AbnormalIntr) { /* Abnormal error summary bit. */
if (csr5 == 0xffffffff)
break;
if (csr5 & TxJabber) tp->stats.tx_errors++;
if (csr5 & TxFIFOUnderflow) {
if ((tp->csr6 & 0xC000) != 0xC000)
tp->csr6 += 0x4000; /* Bump up the Tx threshold */
else
tp->csr6 |= 0x00200000; /* Store-n-forward. */
/* Restart the transmit process. */
tulip_restart_rxtx(tp);
outl(0, ioaddr + CSR1);
}
if (csr5 & (RxDied | RxNoBuf)) {
if (tp->flags & COMET_MAC_ADDR) {
outl(tp->mc_filter[0], ioaddr + 0xAC);
outl(tp->mc_filter[1], ioaddr + 0xB0);
}
}
if (csr5 & RxDied) { /* Missed a Rx frame. */
tp->stats.rx_missed_errors += inl(ioaddr + CSR8) & 0xffff;
#ifdef CONFIG_NET_HW_FLOWCONTROL
if (tp->fc_bit && !test_bit(tp->fc_bit, &netdev_fc_xoff)) {
tp->stats.rx_errors++;
tulip_start_rxtx(tp);
}
#else
tp->stats.rx_errors++;
tulip_start_rxtx(tp);
#endif
}
/*
* NB: t21142_lnk_change() does a del_timer_sync(), so be careful if this
* call is ever done under the spinlock
*/
if (csr5 & (TPLnkPass | TPLnkFail | 0x08000000)) {
if (tp->link_change)
(tp->link_change)(dev, csr5);
}
if (csr5 & SytemError) {
int error = (csr5 >> 23) & 7;
/* oops, we hit a PCI error. The code produced corresponds
* to the reason:
* 0 - parity error
* 1 - master abort
* 2 - target abort
* Note that on parity error, we should do a software reset
* of the chip to get it back into a sane state (according
* to the 21142/3 docs that is).
* -- rmk
*/
printk(KERN_ERR "%s: (%lu) System Error occured (%d)\n",
dev->name, tp->nir, error);
}
/* Clear all error sources, included undocumented ones! */
outl(0x0800f7ba, ioaddr + CSR5);
oi++;
}
if (csr5 & TimerInt) {
if (tulip_debug > 2)
printk(KERN_ERR "%s: Re-enabling interrupts, %8.8x.\n",
dev->name, csr5);
#ifdef CONFIG_NET_HW_FLOWCONTROL
if (tp->fc_bit && (test_bit(tp->fc_bit, &netdev_fc_xoff)))
if (net_ratelimit()) printk("BUG!! enabling interupt when FC off (timerintr.) \n");
#endif
outl(tulip_tbl[tp->chip_id].valid_intrs, ioaddr + CSR7);
tp->ttimer = 0;
oi++;
}
if (tx > maxtx || rx > maxrx || oi > maxoi) {
if (tulip_debug > 1)
printk(KERN_WARNING "%s: Too much work during an interrupt, "
"csr5=0x%8.8x. (%lu) (%d,%d,%d)\n", dev->name, csr5, tp->nir, tx, rx, oi);
/* Acknowledge all interrupt sources. */
outl(0x8001ffff, ioaddr + CSR5);
if (tp->flags & HAS_INTR_MITIGATION) {
#ifdef CONFIG_NET_HW_FLOWCONTROL
if(tp->mit_change) {
outl(mit_table[tp->mit_sel], ioaddr + CSR11);
tp->mit_change = 0;
}
#else
/* Josip Loncaric at ICASE did extensive experimentation
to develop a good interrupt mitigation setting.*/
outl(0x8b240000, ioaddr + CSR11);
#endif
} else if (tp->chip_id == LC82C168) {
/* the LC82C168 doesn't have a hw timer.*/
outl(0x00, ioaddr + CSR7);
mod_timer(&tp->timer, RUN_AT(HZ/50));
} else {
/* Mask all interrupting sources, set timer to
re-enable. */
#ifndef CONFIG_NET_HW_FLOWCONTROL
outl(((~csr5) & 0x0001ebef) | AbnormalIntr | TimerInt, ioaddr + CSR7);
outl(0x0012, ioaddr + CSR11);
#endif
}
break;
}
work_count--;
if (work_count == 0)
break;
csr5 = inl(ioaddr + CSR5);
} while ((csr5 & (NormalIntr|AbnormalIntr)) != 0);
tulip_refill_rx(dev);
/* check if the card is in suspend mode */
entry = tp->dirty_rx % RX_RING_SIZE;
if (tp->rx_buffers[entry].skb == NULL) {
if (tulip_debug > 1)
printk(KERN_WARNING "%s: in rx suspend mode: (%lu) (tp->cur_rx = %u, ttimer = %d, rx = %d) go/stay in suspend mode\n", dev->name, tp->nir, tp->cur_rx, tp->ttimer, rx);
if (tp->chip_id == LC82C168) {
outl(0x00, ioaddr + CSR7);
mod_timer(&tp->timer, RUN_AT(HZ/50));
} else {
if (tp->ttimer == 0 || (inl(ioaddr + CSR11) & 0xffff) == 0) {
if (tulip_debug > 1)
printk(KERN_WARNING "%s: in rx suspend mode: (%lu) set timer\n", dev->name, tp->nir);
outl(tulip_tbl[tp->chip_id].valid_intrs | TimerInt,
ioaddr + CSR7);
outl(TimerInt, ioaddr + CSR5);
outl(12, ioaddr + CSR11);
tp->ttimer = 1;
}
}
}
if ((missed = inl(ioaddr + CSR8) & 0x1ffff)) {
tp->stats.rx_dropped += missed & 0x10000 ? 0x10000 : missed;
}
if (tulip_debug > 4)
printk(KERN_DEBUG "%s: exiting interrupt, csr5=%#4.4x.\n",
dev->name, inl(ioaddr + CSR5));
}
+563
View File
@@ -0,0 +1,563 @@
/*
drivers/net/tulip/media.c
Maintained by Jeff Garzik <jgarzik@mandrakesoft.com>
Copyright 2000,2001 The Linux Kernel Team
Written/copyright 1994-2001 by Donald Becker.
This software may be used and distributed according to the terms
of the GNU General Public License, incorporated herein by reference.
Please refer to Documentation/DocBook/tulip.{pdf,ps,html}
for more information on this driver, or visit the project
Web page at http://sourceforge.net/projects/tulip/
*/
//#include <linux/kernel.h>
#include <linux/mii.h>
#include <linux/init.h>
#include <linux/delay.h>
#include "tulip.h"
/* This is a mysterious value that can be written to CSR11 in the 21040 (only)
to support a pre-NWay full-duplex signaling mechanism using short frames.
No one knows what it should be, but if left at its default value some
10base2(!) packets trigger a full-duplex-request interrupt. */
#define FULL_DUPLEX_MAGIC 0x6969
/* The maximum data clock rate is 2.5 Mhz. The minimum timing is usually
met by back-to-back PCI I/O cycles, but we insert a delay to avoid
"overclocking" issues or future 66Mhz PCI. */
#define mdio_delay() inl(mdio_addr)
/* Read and write the MII registers using software-generated serial
MDIO protocol. It is just different enough from the EEPROM protocol
to not share code. The maxium data clock rate is 2.5 Mhz. */
#define MDIO_SHIFT_CLK 0x10000
#define MDIO_DATA_WRITE0 0x00000
#define MDIO_DATA_WRITE1 0x20000
#define MDIO_ENB 0x00000 /* Ignore the 0x02000 databook setting. */
#define MDIO_ENB_IN 0x40000
#define MDIO_DATA_READ 0x80000
static const unsigned char comet_miireg2offset[32] = {
0xB4, 0xB8, 0xBC, 0xC0, 0xC4, 0xC8, 0xCC, 0, 0,0,0,0, 0,0,0,0,
0,0xD0,0,0, 0,0,0,0, 0,0,0,0, 0, 0xD4, 0xD8, 0xDC, };
/* MII transceiver control section.
Read and write the MII registers using software-generated serial
MDIO protocol. See the MII specifications or DP83840A data sheet
for details. */
int tulip_mdio_read(struct net_device *dev, int phy_id, int location)
{
struct tulip_private *tp = (struct tulip_private *)dev->priv;
int i;
int read_cmd = (0xf6 << 10) | ((phy_id & 0x1f) << 5) | location;
int retval = 0;
long ioaddr = dev->base_addr;
long mdio_addr = ioaddr + CSR9;
unsigned long flags;
if (location & ~0x1f)
return 0xffff;
if (tp->chip_id == COMET && phy_id == 30) {
if (comet_miireg2offset[location])
return inl(ioaddr + comet_miireg2offset[location]);
return 0xffff;
}
spin_lock_irqsave(&tp->mii_lock, flags);
if (tp->chip_id == LC82C168) {
int i = 1000;
outl(0x60020000 + (phy_id<<23) + (location<<18), ioaddr + 0xA0);
inl(ioaddr + 0xA0);
inl(ioaddr + 0xA0);
while (--i > 0) {
barrier();
if ( ! ((retval = inl(ioaddr + 0xA0)) & 0x80000000))
break;
}
spin_unlock_irqrestore(&tp->mii_lock, flags);
return retval & 0xffff;
}
/* Establish sync by sending at least 32 logic ones. */
for (i = 32; i >= 0; i--) {
outl(MDIO_ENB | MDIO_DATA_WRITE1, mdio_addr);
mdio_delay();
outl(MDIO_ENB | MDIO_DATA_WRITE1 | MDIO_SHIFT_CLK, mdio_addr);
mdio_delay();
}
/* Shift the read command bits out. */
for (i = 15; i >= 0; i--) {
int dataval = (read_cmd & (1 << i)) ? MDIO_DATA_WRITE1 : 0;
outl(MDIO_ENB | dataval, mdio_addr);
mdio_delay();
outl(MDIO_ENB | dataval | MDIO_SHIFT_CLK, mdio_addr);
mdio_delay();
}
/* Read the two transition, 16 data, and wire-idle bits. */
for (i = 19; i > 0; i--) {
outl(MDIO_ENB_IN, mdio_addr);
mdio_delay();
retval = (retval << 1) | ((inl(mdio_addr) & MDIO_DATA_READ) ? 1 : 0);
outl(MDIO_ENB_IN | MDIO_SHIFT_CLK, mdio_addr);
mdio_delay();
}
spin_unlock_irqrestore(&tp->mii_lock, flags);
return (retval>>1) & 0xffff;
}
void tulip_mdio_write(struct net_device *dev, int phy_id, int location, int val)
{
struct tulip_private *tp = (struct tulip_private *)dev->priv;
int i;
int cmd = (0x5002 << 16) | ((phy_id & 0x1f) << 23) | (location<<18) | (val & 0xffff);
long ioaddr = dev->base_addr;
long mdio_addr = ioaddr + CSR9;
unsigned long flags;
if (location & ~0x1f)
return;
if (tp->chip_id == COMET && phy_id == 30) {
if (comet_miireg2offset[location])
outl(val, ioaddr + comet_miireg2offset[location]);
return;
}
spin_lock_irqsave(&tp->mii_lock, flags);
if (tp->chip_id == LC82C168) {
int i = 1000;
outl(cmd, ioaddr + 0xA0);
do {
barrier();
if ( ! (inl(ioaddr + 0xA0) & 0x80000000))
break;
} while (--i > 0);
spin_unlock_irqrestore(&tp->mii_lock, flags);
return;
}
/* Establish sync by sending 32 logic ones. */
for (i = 32; i >= 0; i--) {
outl(MDIO_ENB | MDIO_DATA_WRITE1, mdio_addr);
mdio_delay();
outl(MDIO_ENB | MDIO_DATA_WRITE1 | MDIO_SHIFT_CLK, mdio_addr);
mdio_delay();
}
/* Shift the command bits out. */
for (i = 31; i >= 0; i--) {
int dataval = (cmd & (1 << i)) ? MDIO_DATA_WRITE1 : 0;
outl(MDIO_ENB | dataval, mdio_addr);
mdio_delay();
outl(MDIO_ENB | dataval | MDIO_SHIFT_CLK, mdio_addr);
mdio_delay();
}
/* Clear out extra bits. */
for (i = 2; i > 0; i--) {
outl(MDIO_ENB_IN, mdio_addr);
mdio_delay();
outl(MDIO_ENB_IN | MDIO_SHIFT_CLK, mdio_addr);
mdio_delay();
}
spin_unlock_irqrestore(&tp->mii_lock, flags);
}
/* Set up the transceiver control registers for the selected media type. */
void tulip_select_media(struct net_device *dev, int startup)
{
long ioaddr = dev->base_addr;
struct tulip_private *tp = (struct tulip_private *)dev->priv;
struct mediatable *mtable = tp->mtable;
u32 new_csr6;
int i;
if (mtable) {
struct medialeaf *mleaf = &mtable->mleaf[tp->cur_index];
unsigned char *p = mleaf->leafdata;
switch (mleaf->type) {
case 0: /* 21140 non-MII xcvr. */
if (tulip_debug > 1)
printk(KERN_DEBUG "%s: Using a 21140 non-MII transceiver"
" with control setting %2.2x.\n",
dev->name, p[1]);
dev->if_port = p[0];
if (startup)
outl(mtable->csr12dir | 0x100, ioaddr + CSR12);
outl(p[1], ioaddr + CSR12);
new_csr6 = 0x02000000 | ((p[2] & 0x71) << 18);
break;
case 2: case 4: {
u16 setup[5];
u32 csr13val, csr14val, csr15dir, csr15val;
for (i = 0; i < 5; i++)
setup[i] = get_u16(&p[i*2 + 1]);
dev->if_port = p[0] & MEDIA_MASK;
if (tulip_media_cap[dev->if_port] & MediaAlwaysFD)
tp->full_duplex = 1;
if (startup && mtable->has_reset) {
struct medialeaf *rleaf = &mtable->mleaf[mtable->has_reset];
unsigned char *rst = rleaf->leafdata;
if (tulip_debug > 1)
printk(KERN_DEBUG "%s: Resetting the transceiver.\n",
dev->name);
for (i = 0; i < rst[0]; i++)
outl(get_u16(rst + 1 + (i<<1)) << 16, ioaddr + CSR15);
}
if (tulip_debug > 1)
printk(KERN_DEBUG "%s: 21143 non-MII %s transceiver control "
"%4.4x/%4.4x.\n",
dev->name, medianame[dev->if_port], setup[0], setup[1]);
if (p[0] & 0x40) { /* SIA (CSR13-15) setup values are provided. */
csr13val = setup[0];
csr14val = setup[1];
csr15dir = (setup[3]<<16) | setup[2];
csr15val = (setup[4]<<16) | setup[2];
outl(0, ioaddr + CSR13);
outl(csr14val, ioaddr + CSR14);
outl(csr15dir, ioaddr + CSR15); /* Direction */
outl(csr15val, ioaddr + CSR15); /* Data */
outl(csr13val, ioaddr + CSR13);
} else {
csr13val = 1;
csr14val = 0;
csr15dir = (setup[0]<<16) | 0x0008;
csr15val = (setup[1]<<16) | 0x0008;
if (dev->if_port <= 4)
csr14val = t21142_csr14[dev->if_port];
if (startup) {
outl(0, ioaddr + CSR13);
outl(csr14val, ioaddr + CSR14);
}
outl(csr15dir, ioaddr + CSR15); /* Direction */
outl(csr15val, ioaddr + CSR15); /* Data */
if (startup) outl(csr13val, ioaddr + CSR13);
}
if (tulip_debug > 1)
printk(KERN_DEBUG "%s: Setting CSR15 to %8.8x/%8.8x.\n",
dev->name, csr15dir, csr15val);
if (mleaf->type == 4)
new_csr6 = 0x82020000 | ((setup[2] & 0x71) << 18);
else
new_csr6 = 0x82420000;
break;
}
case 1: case 3: {
int phy_num = p[0];
int init_length = p[1];
u16 *misc_info, tmp_info;
dev->if_port = 11;
new_csr6 = 0x020E0000;
if (mleaf->type == 3) { /* 21142 */
u16 *init_sequence = (u16*)(p+2);
u16 *reset_sequence = &((u16*)(p+3))[init_length];
int reset_length = p[2 + init_length*2];
misc_info = reset_sequence + reset_length;
if (startup)
for (i = 0; i < reset_length; i++)
outl(get_u16(&reset_sequence[i]) << 16, ioaddr + CSR15);
for (i = 0; i < init_length; i++)
outl(get_u16(&init_sequence[i]) << 16, ioaddr + CSR15);
} else {
u8 *init_sequence = p + 2;
u8 *reset_sequence = p + 3 + init_length;
int reset_length = p[2 + init_length];
misc_info = (u16*)(reset_sequence + reset_length);
if (startup) {
outl(mtable->csr12dir | 0x100, ioaddr + CSR12);
for (i = 0; i < reset_length; i++)
outl(reset_sequence[i], ioaddr + CSR12);
}
for (i = 0; i < init_length; i++)
outl(init_sequence[i], ioaddr + CSR12);
}
tmp_info = get_u16(&misc_info[1]);
if (tmp_info)
tp->advertising[phy_num] = tmp_info | 1;
if (tmp_info && startup < 2) {
if (tp->mii_advertise == 0)
tp->mii_advertise = tp->advertising[phy_num];
if (tulip_debug > 1)
printk(KERN_DEBUG "%s: Advertising %4.4x on MII %d.\n",
dev->name, tp->mii_advertise, tp->phys[phy_num]);
tulip_mdio_write(dev, tp->phys[phy_num], 4, tp->mii_advertise);
}
break;
}
case 5: case 6: {
u16 setup[5];
new_csr6 = 0; /* FIXME */
for (i = 0; i < 5; i++)
setup[i] = get_u16(&p[i*2 + 1]);
if (startup && mtable->has_reset) {
struct medialeaf *rleaf = &mtable->mleaf[mtable->has_reset];
unsigned char *rst = rleaf->leafdata;
if (tulip_debug > 1)
printk(KERN_DEBUG "%s: Resetting the transceiver.\n",
dev->name);
for (i = 0; i < rst[0]; i++)
outl(get_u16(rst + 1 + (i<<1)) << 16, ioaddr + CSR15);
}
break;
}
default:
printk(KERN_DEBUG "%s: Invalid media table selection %d.\n",
dev->name, mleaf->type);
new_csr6 = 0x020E0000;
}
if (tulip_debug > 1)
printk(KERN_DEBUG "%s: Using media type %s, CSR12 is %2.2x.\n",
dev->name, medianame[dev->if_port],
inl(ioaddr + CSR12) & 0xff);
} else if (tp->chip_id == DC21041) {
int port = dev->if_port <= 4 ? dev->if_port : 0;
if (tulip_debug > 1)
printk(KERN_DEBUG "%s: 21041 using media %s, CSR12 is %4.4x.\n",
dev->name, medianame[port == 3 ? 12: port],
inl(ioaddr + CSR12));
outl(0x00000000, ioaddr + CSR13); /* Reset the serial interface */
outl(t21041_csr14[port], ioaddr + CSR14);
outl(t21041_csr15[port], ioaddr + CSR15);
outl(t21041_csr13[port], ioaddr + CSR13);
new_csr6 = 0x80020000;
} else if (tp->chip_id == LC82C168) {
if (startup && ! tp->medialock)
dev->if_port = tp->mii_cnt ? 11 : 0;
if (tulip_debug > 1)
printk(KERN_DEBUG "%s: PNIC PHY status is %3.3x, media %s.\n",
dev->name, inl(ioaddr + 0xB8), medianame[dev->if_port]);
if (tp->mii_cnt) {
new_csr6 = 0x810C0000;
outl(0x0001, ioaddr + CSR15);
outl(0x0201B07A, ioaddr + 0xB8);
} else if (startup) {
/* Start with 10mbps to do autonegotiation. */
outl(0x32, ioaddr + CSR12);
new_csr6 = 0x00420000;
outl(0x0001B078, ioaddr + 0xB8);
outl(0x0201B078, ioaddr + 0xB8);
} else if (dev->if_port == 3 || dev->if_port == 5) {
outl(0x33, ioaddr + CSR12);
new_csr6 = 0x01860000;
/* Trigger autonegotiation. */
outl(startup ? 0x0201F868 : 0x0001F868, ioaddr + 0xB8);
} else {
outl(0x32, ioaddr + CSR12);
new_csr6 = 0x00420000;
outl(0x1F078, ioaddr + 0xB8);
}
} else if (tp->chip_id == DC21040) { /* 21040 */
/* Turn on the xcvr interface. */
int csr12 = inl(ioaddr + CSR12);
if (tulip_debug > 1)
printk(KERN_DEBUG "%s: 21040 media type is %s, CSR12 is %2.2x.\n",
dev->name, medianame[dev->if_port], csr12);
if (tulip_media_cap[dev->if_port] & MediaAlwaysFD)
tp->full_duplex = 1;
new_csr6 = 0x20000;
/* Set the full duplux match frame. */
outl(FULL_DUPLEX_MAGIC, ioaddr + CSR11);
outl(0x00000000, ioaddr + CSR13); /* Reset the serial interface */
if (t21040_csr13[dev->if_port] & 8) {
outl(0x0705, ioaddr + CSR14);
outl(0x0006, ioaddr + CSR15);
} else {
outl(0xffff, ioaddr + CSR14);
outl(0x0000, ioaddr + CSR15);
}
outl(0x8f01 | t21040_csr13[dev->if_port], ioaddr + CSR13);
} else { /* Unknown chip type with no media table. */
if (tp->default_port == 0)
dev->if_port = tp->mii_cnt ? 11 : 3;
if (tulip_media_cap[dev->if_port] & MediaIsMII) {
new_csr6 = 0x020E0000;
} else if (tulip_media_cap[dev->if_port] & MediaIsFx) {
new_csr6 = 0x028600000;
} else
new_csr6 = 0x038600000;
if (tulip_debug > 1)
printk(KERN_DEBUG "%s: No media description table, assuming "
"%s transceiver, CSR12 %2.2x.\n",
dev->name, medianame[dev->if_port],
inl(ioaddr + CSR12));
}
tp->csr6 = new_csr6 | (tp->csr6 & 0xfdff) | (tp->full_duplex ? 0x0200 : 0);
return;
}
/*
Check the MII negotiated duplex and change the CSR6 setting if
required.
Return 0 if everything is OK.
Return < 0 if the transceiver is missing or has no link beat.
*/
int tulip_check_duplex(struct net_device *dev)
{
struct tulip_private *tp = dev->priv;
unsigned int bmsr, lpa, negotiated, new_csr6;
bmsr = tulip_mdio_read(dev, tp->phys[0], MII_BMSR);
lpa = tulip_mdio_read(dev, tp->phys[0], MII_LPA);
if (tulip_debug > 1)
printk(KERN_INFO "%s: MII status %4.4x, Link partner report "
"%4.4x.\n", dev->name, bmsr, lpa);
if (bmsr == 0xffff)
return -2;
if ((bmsr & BMSR_LSTATUS) == 0) {
int new_bmsr = tulip_mdio_read(dev, tp->phys[0], MII_BMSR);
if ((new_bmsr & BMSR_LSTATUS) == 0) {
if (tulip_debug > 1)
printk(KERN_INFO "%s: No link beat on the MII interface,"
" status %4.4x.\n", dev->name, new_bmsr);
return -1;
}
}
negotiated = lpa & tp->advertising[0];
tp->full_duplex = mii_duplex(tp->full_duplex_lock, negotiated);
new_csr6 = tp->csr6;
if (negotiated & LPA_100) new_csr6 &= ~TxThreshold;
else new_csr6 |= TxThreshold;
if (tp->full_duplex) new_csr6 |= FullDuplex;
else new_csr6 &= ~FullDuplex;
if (new_csr6 != tp->csr6) {
tp->csr6 = new_csr6;
tulip_restart_rxtx(tp);
if (tulip_debug > 0)
printk(KERN_INFO "%s: Setting %s-duplex based on MII"
"#%d link partner capability of %4.4x.\n",
dev->name, tp->full_duplex ? "full" : "half",
tp->phys[0], lpa);
return 1;
}
return 0;
}
void __devinit tulip_find_mii (struct net_device *dev, int board_idx)
{
struct tulip_private *tp = dev->priv;
int phyn, phy_idx = 0;
int mii_reg0;
int mii_advert;
unsigned int to_advert, new_bmcr, ane_switch;
/* Find the connected MII xcvrs.
Doing this in open() would allow detecting external xcvrs later,
but takes much time. */
for (phyn = 1; phyn <= 32 && phy_idx < sizeof (tp->phys); phyn++) {
int phy = phyn & 0x1f;
int mii_status = tulip_mdio_read (dev, phy, MII_BMSR);
if ((mii_status & 0x8301) == 0x8001 ||
((mii_status & BMSR_100BASE4) == 0
&& (mii_status & 0x7800) != 0)) {
/* preserve Becker logic, gain indentation level */
} else {
continue;
}
mii_reg0 = tulip_mdio_read (dev, phy, MII_BMCR);
mii_advert = tulip_mdio_read (dev, phy, MII_ADVERTISE);
ane_switch = 0;
/* if not advertising at all, gen an
* advertising value from the capability
* bits in BMSR
*/
if ((mii_advert & ADVERTISE_ALL) == 0) {
unsigned int tmpadv = tulip_mdio_read (dev, phy, MII_BMSR);
mii_advert = ((tmpadv >> 6) & 0x3e0) | 1;
}
if (tp->mii_advertise) {
tp->advertising[phy_idx] =
to_advert = tp->mii_advertise;
} else if (tp->advertising[phy_idx]) {
to_advert = tp->advertising[phy_idx];
} else {
tp->advertising[phy_idx] =
tp->mii_advertise =
to_advert = mii_advert;
}
tp->phys[phy_idx++] = phy;
printk (KERN_INFO "tulip%d: MII transceiver #%d "
"config %4.4x status %4.4x advertising %4.4x.\n",
board_idx, phy, mii_reg0, mii_status, mii_advert);
/* Fixup for DLink with miswired PHY. */
if (mii_advert != to_advert) {
printk (KERN_DEBUG "tulip%d: Advertising %4.4x on PHY %d,"
" previously advertising %4.4x.\n",
board_idx, to_advert, phy, mii_advert);
tulip_mdio_write (dev, phy, 4, to_advert);
}
/* Enable autonegotiation: some boards default to off. */
if (tp->default_port == 0) {
new_bmcr = mii_reg0 | BMCR_ANENABLE;
if (new_bmcr != mii_reg0) {
new_bmcr |= BMCR_ANRESTART;
ane_switch = 1;
}
}
/* ...or disable nway, if forcing media */
else {
new_bmcr = mii_reg0 & ~BMCR_ANENABLE;
if (new_bmcr != mii_reg0)
ane_switch = 1;
}
/* clear out bits we never want at this point */
new_bmcr &= ~(BMCR_CTST | BMCR_FULLDPLX | BMCR_ISOLATE |
BMCR_PDOWN | BMCR_SPEED100 | BMCR_LOOPBACK |
BMCR_RESET);
if (tp->full_duplex)
new_bmcr |= BMCR_FULLDPLX;
if (tulip_media_cap[tp->default_port] & MediaIs100)
new_bmcr |= BMCR_SPEED100;
if (new_bmcr != mii_reg0) {
/* some phys need the ANE switch to
* happen before forced media settings
* will "take." However, we write the
* same value twice in order not to
* confuse the sane phys.
*/
if (ane_switch) {
tulip_mdio_write (dev, phy, MII_BMCR, new_bmcr);
udelay (10);
}
tulip_mdio_write (dev, phy, MII_BMCR, new_bmcr);
}
}
tp->mii_cnt = phy_idx;
if (tp->mtable && tp->mtable->has_mii && phy_idx == 0) {
printk (KERN_INFO "tulip%d: ***WARNING***: No MII transceiver found!\n",
board_idx);
tp->phys[0] = 1;
}
}
+171
View File
@@ -0,0 +1,171 @@
/*
drivers/net/tulip/pnic.c
Maintained by Jeff Garzik <jgarzik@mandrakesoft.com>
Copyright 2000,2001 The Linux Kernel Team
Written/copyright 1994-2001 by Donald Becker.
This software may be used and distributed according to the terms
of the GNU General Public License, incorporated herein by reference.
Please refer to Documentation/DocBook/tulip.{pdf,ps,html}
for more information on this driver, or visit the project
Web page at http://sourceforge.net/projects/tulip/
*/
//#include <linux/kernel.h>
#include "tulip.h"
void pnic_do_nway(struct net_device *dev)
{
struct tulip_private *tp = (struct tulip_private *)dev->priv;
long ioaddr = dev->base_addr;
u32 phy_reg = inl(ioaddr + 0xB8);
u32 new_csr6 = tp->csr6 & ~0x40C40200;
if (phy_reg & 0x78000000) { /* Ignore baseT4 */
if (phy_reg & 0x20000000) dev->if_port = 5;
else if (phy_reg & 0x40000000) dev->if_port = 3;
else if (phy_reg & 0x10000000) dev->if_port = 4;
else if (phy_reg & 0x08000000) dev->if_port = 0;
tp->nwayset = 1;
new_csr6 = (dev->if_port & 1) ? 0x01860000 : 0x00420000;
outl(0x32 | (dev->if_port & 1), ioaddr + CSR12);
if (dev->if_port & 1)
outl(0x1F868, ioaddr + 0xB8);
if (phy_reg & 0x30000000) {
tp->full_duplex = 1;
new_csr6 |= 0x00000200;
}
if (tulip_debug > 1)
printk(KERN_DEBUG "%s: PNIC autonegotiated status %8.8x, %s.\n",
dev->name, phy_reg, medianame[dev->if_port]);
if (tp->csr6 != new_csr6) {
tp->csr6 = new_csr6;
/* Restart Tx */
tulip_restart_rxtx(tp);
dev->trans_start = jiffies;
}
}
}
void pnic_lnk_change(struct net_device *dev, int csr5)
{
struct tulip_private *tp = (struct tulip_private *)dev->priv;
long ioaddr = dev->base_addr;
int phy_reg = inl(ioaddr + 0xB8);
if (tulip_debug > 1)
printk(KERN_DEBUG "%s: PNIC link changed state %8.8x, CSR5 %8.8x.\n",
dev->name, phy_reg, csr5);
if (inl(ioaddr + CSR5) & TPLnkFail) {
outl((inl(ioaddr + CSR7) & ~TPLnkFail) | TPLnkPass, ioaddr + CSR7);
/* If we use an external MII, then we mustn't use the
* internal negotiation.
*/
if (tulip_media_cap[dev->if_port] & MediaIsMII)
return;
if (! tp->nwayset || jiffies - dev->trans_start > 1*HZ) {
tp->csr6 = 0x00420000 | (tp->csr6 & 0x0000fdff);
outl(tp->csr6, ioaddr + CSR6);
outl(0x30, ioaddr + CSR12);
outl(0x0201F078, ioaddr + 0xB8); /* Turn on autonegotiation. */
dev->trans_start = jiffies;
}
} else if (inl(ioaddr + CSR5) & TPLnkPass) {
if (tulip_media_cap[dev->if_port] & MediaIsMII) {
spin_lock(&tp->lock);
tulip_check_duplex(dev);
spin_unlock(&tp->lock);
} else {
pnic_do_nway(dev);
}
outl((inl(ioaddr + CSR7) & ~TPLnkPass) | TPLnkFail, ioaddr + CSR7);
}
}
void pnic_timer(unsigned long data)
{
struct net_device *dev = (struct net_device *)data;
struct tulip_private *tp = (struct tulip_private *)dev->priv;
long ioaddr = dev->base_addr;
int next_tick = 60*HZ;
if(!inl(ioaddr + CSR7)) {
/* the timer was called due to a work overflow
* in the interrupt handler. Skip the connection
* checks, the nic is definitively speaking with
* his link partner.
*/
goto too_good_connection;
}
if (tulip_media_cap[dev->if_port] & MediaIsMII) {
spin_lock_irq(&tp->lock);
if (tulip_check_duplex(dev) > 0)
next_tick = 3*HZ;
spin_unlock_irq(&tp->lock);
} else {
int csr12 = inl(ioaddr + CSR12);
int new_csr6 = tp->csr6 & ~0x40C40200;
int phy_reg = inl(ioaddr + 0xB8);
int csr5 = inl(ioaddr + CSR5);
if (tulip_debug > 1)
printk(KERN_DEBUG "%s: PNIC timer PHY status %8.8x, %s "
"CSR5 %8.8x.\n",
dev->name, phy_reg, medianame[dev->if_port], csr5);
if (phy_reg & 0x04000000) { /* Remote link fault */
outl(0x0201F078, ioaddr + 0xB8);
next_tick = 1*HZ;
tp->nwayset = 0;
} else if (phy_reg & 0x78000000) { /* Ignore baseT4 */
pnic_do_nway(dev);
next_tick = 60*HZ;
} else if (csr5 & TPLnkFail) { /* 100baseTx link beat */
if (tulip_debug > 1)
printk(KERN_DEBUG "%s: %s link beat failed, CSR12 %4.4x, "
"CSR5 %8.8x, PHY %3.3x.\n",
dev->name, medianame[dev->if_port], csr12,
inl(ioaddr + CSR5), inl(ioaddr + 0xB8));
next_tick = 3*HZ;
if (tp->medialock) {
} else if (tp->nwayset && (dev->if_port & 1)) {
next_tick = 1*HZ;
} else if (dev->if_port == 0) {
dev->if_port = 3;
outl(0x33, ioaddr + CSR12);
new_csr6 = 0x01860000;
outl(0x1F868, ioaddr + 0xB8);
} else {
dev->if_port = 0;
outl(0x32, ioaddr + CSR12);
new_csr6 = 0x00420000;
outl(0x1F078, ioaddr + 0xB8);
}
if (tp->csr6 != new_csr6) {
tp->csr6 = new_csr6;
/* Restart Tx */
tulip_restart_rxtx(tp);
dev->trans_start = jiffies;
if (tulip_debug > 1)
printk(KERN_INFO "%s: Changing PNIC configuration to %s "
"%s-duplex, CSR6 %8.8x.\n",
dev->name, medianame[dev->if_port],
tp->full_duplex ? "full" : "half", new_csr6);
}
}
}
too_good_connection:
mod_timer(&tp->timer, RUN_AT(next_tick));
if(!inl(ioaddr + CSR7)) {
if (tulip_debug > 1)
printk(KERN_INFO "%s: sw timer wakeup.\n", dev->name);
disable_irq(dev->irq);
tulip_refill_rx(dev);
enable_irq(dev->irq);
outl(tulip_tbl[tp->chip_id].valid_intrs, ioaddr + CSR7);
}
}
+407
View File
@@ -0,0 +1,407 @@
/*
drivers/net/tulip/pnic2.c
Maintained by Jeff Garzik <jgarzik@mandrakesoft.com>
Copyright 2000,2001 The Linux Kernel Team
Written/copyright 1994-2001 by Donald Becker.
Modified to hep support PNIC_II by Kevin B. Hendricks
This software may be used and distributed according to the terms
of the GNU General Public License, incorporated herein by reference.
Please refer to Documentation/DocBook/tulip.{pdf,ps,html}
for more information on this driver, or visit the project
Web page at http://sourceforge.net/projects/tulip/
*/
/* Understanding the PNIC_II - everything is this file is based
* on the PNIC_II_PDF datasheet which is sorely lacking in detail
*
* As I understand things, here are the registers and bits that
* explain the masks and constants used in this file that are
* either different from the 21142/3 or important for basic operation.
*
*
* CSR 6 (mask = 0xfe3bd1fd of bits not to change)
* -----
* Bit 24 - SCR
* Bit 23 - PCS
* Bit 22 - TTM (Trasmit Threshold Mode)
* Bit 18 - Port Select
* Bit 13 - Start - 1, Stop - 0 Transmissions
* Bit 11:10 - Loop Back Operation Mode
* Bit 9 - Full Duplex mode (Advertise 10BaseT-FD is CSR14<7> is set)
* Bit 1 - Start - 1, Stop - 0 Receive
*
*
* CSR 14 (mask = 0xfff0ee39 of bits not to change)
* ------
* Bit 19 - PAUSE-Pause
* Bit 18 - Advertise T4
* Bit 17 - Advertise 100baseTx-FD
* Bit 16 - Advertise 100baseTx-HD
* Bit 12 - LTE - Link Test Enable
* Bit 7 - ANE - Auto Negotiate Enable
* Bit 6 - HDE - Advertise 10baseT-HD
* Bit 2 - Reset to Power down - kept as 1 for normal operation
* Bit 1 - Loop Back enable for 10baseT MCC
*
*
* CSR 12
* ------
* Bit 25 - Partner can do T4
* Bit 24 - Partner can do 100baseTx-FD
* Bit 23 - Partner can do 100baseTx-HD
* Bit 22 - Partner can do 10baseT-FD
* Bit 21 - Partner can do 10baseT-HD
* Bit 15 - LPN is 1 if all above bits are valid other wise 0
* Bit 14:12 - autonegotiation state (write 001 to start autonegotiate)
* Bit 3 - Autopolarity state
* Bit 2 - LS10B - link state of 10baseT 0 - good, 1 - failed
* Bit 1 - LS100B - link state of 100baseT 0 - good, 1- faild
*
*
* Data Port Selection Info
*-------------------------
*
* CSR14<7> CSR6<18> CSR6<22> CSR6<23> CSR6<24> MODE/PORT
* 1 0 0 (X) 0 (X) 1 NWAY
* 0 0 1 0 (X) 0 10baseT
* 0 1 0 1 1 (X) 100baseT
*
*
*/
#include "tulip.h"
#include <linux/pci.h>
#include <linux/delay.h>
void pnic2_timer(unsigned long data)
{
struct net_device *dev = (struct net_device *)data;
struct tulip_private *tp = (struct tulip_private *)dev->priv;
long ioaddr = dev->base_addr;
int next_tick = 60*HZ;
if (tulip_debug > 3)
printk(KERN_INFO"%s: PNIC2 negotiation status %8.8x.\n",
dev->name,inl(ioaddr + CSR12));
if (next_tick) {
mod_timer(&tp->timer, RUN_AT(next_tick));
}
}
void pnic2_start_nway(struct net_device *dev)
{
struct tulip_private *tp = (struct tulip_private *)dev->priv;
long ioaddr = dev->base_addr;
int csr14;
int csr12;
/* set up what to advertise during the negotiation */
/* load in csr14 and mask off bits not to touch
* comment at top of file explains mask value
*/
csr14 = (inl(ioaddr + CSR14) & 0xfff0ee39);
/* bit 17 - advetise 100baseTx-FD */
if (tp->sym_advertise & 0x0100) csr14 |= 0x00020000;
/* bit 16 - advertise 100baseTx-HD */
if (tp->sym_advertise & 0x0080) csr14 |= 0x00010000;
/* bit 6 - advertise 10baseT-HD */
if (tp->sym_advertise & 0x0020) csr14 |= 0x00000040;
/* Now set bit 12 Link Test Enable, Bit 7 Autonegotiation Enable
* and bit 0 Don't PowerDown 10baseT
*/
csr14 |= 0x00001184;
if (tulip_debug > 1)
printk(KERN_DEBUG "%s: Restarting PNIC2 autonegotiation, "
"csr14=%8.8x.\n", dev->name, csr14);
/* tell pnic2_lnk_change we are doing an nway negotiation */
dev->if_port = 0;
tp->nway = tp->mediasense = 1;
tp->nwayset = tp->lpar = 0;
/* now we have to set up csr6 for NWAY state */
tp->csr6 = inl(ioaddr + CSR6);
if (tulip_debug > 1)
printk(KERN_DEBUG "%s: On Entry to Nway, "
"csr6=%8.8x.\n", dev->name, tp->csr6);
/* mask off any bits not to touch
* comment at top of file explains mask value
*/
tp->csr6 = tp->csr6 & 0xfe3bd1fd;
/* don't forget that bit 9 is also used for advertising */
/* advertise 10baseT-FD for the negotiation (bit 9) */
if (tp->sym_advertise & 0x0040) tp->csr6 |= 0x00000200;
/* set bit 24 for nway negotiation mode ...
* see Data Port Selection comment at top of file
* and "Stop" - reset both Transmit (bit 13) and Receive (bit 1)
*/
tp->csr6 |= 0x01000000;
outl(csr14, ioaddr + CSR14);
outl(tp->csr6, ioaddr + CSR6);
udelay(100);
/* all set up so now force the negotiation to begin */
/* read in current values and mask off all but the
* Autonegotiation bits 14:12. Writing a 001 to those bits
* should start the autonegotiation
*/
csr12 = (inl(ioaddr + CSR12) & 0xffff8fff);
csr12 |= 0x1000;
outl(csr12, ioaddr + CSR12);
}
void pnic2_lnk_change(struct net_device *dev, int csr5)
{
struct tulip_private *tp = (struct tulip_private *)dev->priv;
long ioaddr = dev->base_addr;
int csr14;
/* read the staus register to find out what is up */
int csr12 = inl(ioaddr + CSR12);
if (tulip_debug > 1)
printk(KERN_INFO"%s: PNIC2 link status interrupt %8.8x, "
" CSR5 %x, %8.8x.\n", dev->name, csr12,
csr5, inl(ioaddr + CSR14));
/* If NWay finished and we have a negotiated partner capability.
* check bits 14:12 for bit pattern 101 - all is good
*/
if (tp->nway && !tp->nwayset) {
/* we did an auto negotiation */
if ((csr12 & 0x7000) == 0x5000) {
/* negotiation ended successfully */
/* get the link partners reply and mask out all but
* bits 24-21 which show the partners capabilites
* and match those to what we advertised
*
* then begin to interpret the results of the negotiation.
* Always go in this order : (we are ignoring T4 for now)
* 100baseTx-FD, 100baseTx-HD, 10baseT-FD, 10baseT-HD
*/
int negotiated = ((csr12 >> 16) & 0x01E0) & tp->sym_advertise;
tp->lpar = (csr12 >> 16);
tp->nwayset = 1;
if (negotiated & 0x0100) dev->if_port = 5;
else if (negotiated & 0x0080) dev->if_port = 3;
else if (negotiated & 0x0040) dev->if_port = 4;
else if (negotiated & 0x0020) dev->if_port = 0;
else {
if (tulip_debug > 1)
printk(KERN_INFO "%s: funny autonegotiate result "
"csr12 %8.8x advertising %4.4x\n",
dev->name, csr12, tp->sym_advertise);
tp->nwayset = 0;
/* so check if 100baseTx link state is okay */
if ((csr12 & 2) == 0 && (tp->sym_advertise & 0x0180))
dev->if_port = 3;
}
/* now record the duplex that was negotiated */
tp->full_duplex = 0;
if ((dev->if_port == 4) || (dev->if_port == 5))
tp->full_duplex = 1;
if (tulip_debug > 1) {
if (tp->nwayset)
printk(KERN_INFO "%s: Switching to %s based on link "
"negotiation %4.4x & %4.4x = %4.4x.\n",
dev->name, medianame[dev->if_port],
tp->sym_advertise, tp->lpar, negotiated);
}
/* remember to turn off bit 7 - autonegotiate
* enable so we can properly end nway mode and
* set duplex (ie. use csr6<9> again)
*/
csr14 = (inl(ioaddr + CSR14) & 0xffffff7f);
outl(csr14,ioaddr + CSR14);
/* now set the data port and operating mode
* (see the Data Port Selection comments at
* the top of the file
*/
/* get current csr6 and mask off bits not to touch */
/* see comment at top of file */
tp->csr6 = (inl(ioaddr + CSR6) & 0xfe3bd1fd);
/* so if using if_port 3 or 5 then select the 100baseT
* port else select the 10baseT port.
* See the Data Port Selection table at the top
* of the file which was taken from the PNIC_II.PDF
* datasheet
*/
if (dev->if_port & 1) tp->csr6 |= 0x01840000;
else tp->csr6 |= 0x00400000;
/* now set the full duplex bit appropriately */
if (tp->full_duplex) tp->csr6 |= 0x00000200;
outl(1, ioaddr + CSR13);
if (tulip_debug > 2)
printk(KERN_DEBUG "%s: Setting CSR6 %8.8x/%x CSR12 "
"%8.8x.\n", dev->name, tp->csr6,
inl(ioaddr + CSR6), inl(ioaddr + CSR12));
/* now the following actually writes out the
* new csr6 values
*/
tulip_start_rxtx(tp);
return;
} else {
printk(KERN_INFO "%s: Autonegotiation failed, "
"using %s, link beat status %4.4x.\n",
dev->name, medianame[dev->if_port], csr12);
/* remember to turn off bit 7 - autonegotiate
* enable so we don't forget
*/
csr14 = (inl(ioaddr + CSR14) & 0xffffff7f);
outl(csr14,ioaddr + CSR14);
/* what should we do when autonegotiate fails?
* should we try again or default to baseline
* case. I just don't know.
*
* for now default to some baseline case
*/
dev->if_port = 0;
tp->nway = 0;
tp->nwayset = 1;
/* set to 10baseTx-HD - see Data Port Selection
* comment given at the top of the file
*/
tp->csr6 = (inl(ioaddr + CSR6) & 0xfe3bd1fd);
tp->csr6 |= 0x00400000;
tulip_restart_rxtx(tp);
return;
}
}
if ((tp->nwayset && (csr5 & 0x08000000)
&& (dev->if_port == 3 || dev->if_port == 5)
&& (csr12 & 2) == 2) || (tp->nway && (csr5 & (TPLnkFail)))) {
/* Link blew? Maybe restart NWay. */
if (tulip_debug > 2)
printk(KERN_DEBUG "%s: Ugh! Link blew?\n", dev->name);
del_timer_sync(&tp->timer);
pnic2_start_nway(dev);
tp->timer.expires = RUN_AT(3*HZ);
add_timer(&tp->timer);
return;
}
if (dev->if_port == 3 || dev->if_port == 5) {
/* we are at 100mb and a potential link change occurred */
if (tulip_debug > 1)
printk(KERN_INFO"%s: PNIC2 %s link beat %s.\n",
dev->name, medianame[dev->if_port],
(csr12 & 2) ? "failed" : "good");
/* check 100 link beat */
tp->nway = 0;
tp->nwayset = 1;
/* if failed then try doing an nway to get in sync */
if ((csr12 & 2) && ! tp->medialock) {
del_timer_sync(&tp->timer);
pnic2_start_nway(dev);
tp->timer.expires = RUN_AT(3*HZ);
add_timer(&tp->timer);
}
return;
}
if (dev->if_port == 0 || dev->if_port == 4) {
/* we are at 10mb and a potential link change occurred */
if (tulip_debug > 1)
printk(KERN_INFO"%s: PNIC2 %s link beat %s.\n",
dev->name, medianame[dev->if_port],
(csr12 & 4) ? "failed" : "good");
tp->nway = 0;
tp->nwayset = 1;
/* if failed, try doing an nway to get in sync */
if ((csr12 & 4) && ! tp->medialock) {
del_timer_sync(&tp->timer);
pnic2_start_nway(dev);
tp->timer.expires = RUN_AT(3*HZ);
add_timer(&tp->timer);
}
return;
}
if (tulip_debug > 1)
printk(KERN_INFO"%s: PNIC2 Link Change Default?\n",dev->name);
/* if all else fails default to trying 10baseT-HD */
dev->if_port = 0;
/* make sure autonegotiate enable is off */
csr14 = (inl(ioaddr + CSR14) & 0xffffff7f);
outl(csr14,ioaddr + CSR14);
/* set to 10baseTx-HD - see Data Port Selection
* comment given at the top of the file
*/
tp->csr6 = (inl(ioaddr + CSR6) & 0xfe3bd1fd);
tp->csr6 |= 0x00400000;
tulip_restart_rxtx(tp);
}
+220
View File
@@ -0,0 +1,220 @@
/*
drivers/net/tulip/timer.c
Maintained by Jeff Garzik <jgarzik@mandrakesoft.com>
Copyright 2000,2001 The Linux Kernel Team
Written/copyright 1994-2001 by Donald Becker.
This software may be used and distributed according to the terms
of the GNU General Public License, incorporated herein by reference.
Please refer to Documentation/DocBook/tulip.{pdf,ps,html}
for more information on this driver, or visit the project
Web page at http://sourceforge.net/projects/tulip/
*/
#include "tulip.h"
void tulip_timer(unsigned long data)
{
struct net_device *dev = (struct net_device *)data;
struct tulip_private *tp = (struct tulip_private *)dev->priv;
long ioaddr = dev->base_addr;
u32 csr12 = inl(ioaddr + CSR12);
int next_tick = 2*HZ;
if (tulip_debug > 2) {
printk(KERN_DEBUG "%s: Media selection tick, %s, status %8.8x mode"
" %8.8x SIA %8.8x %8.8x %8.8x %8.8x.\n",
dev->name, medianame[dev->if_port], inl(ioaddr + CSR5),
inl(ioaddr + CSR6), csr12, inl(ioaddr + CSR13),
inl(ioaddr + CSR14), inl(ioaddr + CSR15));
}
switch (tp->chip_id) {
case DC21040:
if (!tp->medialock && csr12 & 0x0002) { /* Network error */
printk(KERN_INFO "%s: No link beat found.\n",
dev->name);
dev->if_port = (dev->if_port == 2 ? 0 : 2);
tulip_select_media(dev, 0);
dev->trans_start = jiffies;
}
break;
case DC21041:
if (tulip_debug > 2)
printk(KERN_DEBUG "%s: 21041 media tick CSR12 %8.8x.\n",
dev->name, csr12);
if (tp->medialock) break;
switch (dev->if_port) {
case 0: case 3: case 4:
if (csr12 & 0x0004) { /*LnkFail */
/* 10baseT is dead. Check for activity on alternate port. */
tp->mediasense = 1;
if (csr12 & 0x0200)
dev->if_port = 2;
else
dev->if_port = 1;
printk(KERN_INFO "%s: No 21041 10baseT link beat, Media switched to %s.\n",
dev->name, medianame[dev->if_port]);
outl(0, ioaddr + CSR13); /* Reset */
outl(t21041_csr14[dev->if_port], ioaddr + CSR14);
outl(t21041_csr15[dev->if_port], ioaddr + CSR15);
outl(t21041_csr13[dev->if_port], ioaddr + CSR13);
next_tick = 10*HZ; /* 2.4 sec. */
} else
next_tick = 30*HZ;
break;
case 1: /* 10base2 */
case 2: /* AUI */
if (csr12 & 0x0100) {
next_tick = (30*HZ); /* 30 sec. */
tp->mediasense = 0;
} else if ((csr12 & 0x0004) == 0) {
printk(KERN_INFO "%s: 21041 media switched to 10baseT.\n",
dev->name);
dev->if_port = 0;
tulip_select_media(dev, 0);
next_tick = (24*HZ)/10; /* 2.4 sec. */
} else if (tp->mediasense || (csr12 & 0x0002)) {
dev->if_port = 3 - dev->if_port; /* Swap ports. */
tulip_select_media(dev, 0);
next_tick = 20*HZ;
} else {
next_tick = 20*HZ;
}
break;
}
break;
case DC21140:
case DC21142:
case MX98713:
case COMPEX9881:
case DM910X:
default: {
struct medialeaf *mleaf;
unsigned char *p;
if (tp->mtable == NULL) { /* No EEPROM info, use generic code. */
/* Not much that can be done.
Assume this a generic MII or SYM transceiver. */
next_tick = 60*HZ;
if (tulip_debug > 2)
printk(KERN_DEBUG "%s: network media monitor CSR6 %8.8x "
"CSR12 0x%2.2x.\n",
dev->name, inl(ioaddr + CSR6), csr12 & 0xff);
break;
}
mleaf = &tp->mtable->mleaf[tp->cur_index];
p = mleaf->leafdata;
switch (mleaf->type) {
case 0: case 4: {
/* Type 0 serial or 4 SYM transceiver. Check the link beat bit. */
int offset = mleaf->type == 4 ? 5 : 2;
s8 bitnum = p[offset];
if (p[offset+1] & 0x80) {
if (tulip_debug > 1)
printk(KERN_DEBUG"%s: Transceiver monitor tick "
"CSR12=%#2.2x, no media sense.\n",
dev->name, csr12);
if (mleaf->type == 4) {
if (mleaf->media == 3 && (csr12 & 0x02))
goto select_next_media;
}
break;
}
if (tulip_debug > 2)
printk(KERN_DEBUG "%s: Transceiver monitor tick: CSR12=%#2.2x"
" bit %d is %d, expecting %d.\n",
dev->name, csr12, (bitnum >> 1) & 7,
(csr12 & (1 << ((bitnum >> 1) & 7))) != 0,
(bitnum >= 0));
/* Check that the specified bit has the proper value. */
if ((bitnum < 0) !=
((csr12 & (1 << ((bitnum >> 1) & 7))) != 0)) {
if (tulip_debug > 2)
printk(KERN_DEBUG "%s: Link beat detected for %s.\n", dev->name,
medianame[mleaf->media & MEDIA_MASK]);
if ((p[2] & 0x61) == 0x01) /* Bogus Znyx board. */
goto actually_mii;
/* netif_carrier_on(dev); */
break;
}
/* netif_carrier_off(dev); */
if (tp->medialock)
break;
select_next_media:
if (--tp->cur_index < 0) {
/* We start again, but should instead look for default. */
tp->cur_index = tp->mtable->leafcount - 1;
}
dev->if_port = tp->mtable->mleaf[tp->cur_index].media;
if (tulip_media_cap[dev->if_port] & MediaIsFD)
goto select_next_media; /* Skip FD entries. */
if (tulip_debug > 1)
printk(KERN_DEBUG "%s: No link beat on media %s,"
" trying transceiver type %s.\n",
dev->name, medianame[mleaf->media & MEDIA_MASK],
medianame[tp->mtable->mleaf[tp->cur_index].media]);
tulip_select_media(dev, 0);
/* Restart the transmit process. */
tulip_restart_rxtx(tp);
next_tick = (24*HZ)/10;
break;
}
case 1: case 3: /* 21140, 21142 MII */
actually_mii:
if (tulip_check_duplex(dev) < 0)
{ /* netif_carrier_off(dev); */ }
else
{ /* netif_carrier_on(dev); */ }
next_tick = 60*HZ;
break;
case 2: /* 21142 serial block has no link beat. */
default:
break;
}
}
break;
}
/* mod_timer synchronizes us with potential add_timer calls
* from interrupts.
*/
mod_timer(&tp->timer, RUN_AT(next_tick));
}
void mxic_timer(unsigned long data)
{
struct net_device *dev = (struct net_device *)data;
struct tulip_private *tp = (struct tulip_private *)dev->priv;
long ioaddr = dev->base_addr;
int next_tick = 60*HZ;
if (tulip_debug > 3) {
printk(KERN_INFO"%s: MXIC negotiation status %8.8x.\n", dev->name,
inl(ioaddr + CSR12));
}
if (next_tick) {
mod_timer(&tp->timer, RUN_AT(next_tick));
}
}
void comet_timer(unsigned long data)
{
struct net_device *dev = (struct net_device *)data;
struct tulip_private *tp = (struct tulip_private *)dev->priv;
long ioaddr = dev->base_addr;
int next_tick = 60*HZ;
if (tulip_debug > 1)
printk(KERN_DEBUG "%s: Comet link status %4.4x partner capability "
"%4.4x.\n",
dev->name, inl(ioaddr + 0xB8), inl(ioaddr + 0xC8));
/* mod_timer synchronizes us with potential add_timer calls
* from interrupts.
*/
mod_timer(&tp->timer, RUN_AT(next_tick));
}
+499
View File
@@ -0,0 +1,499 @@
/*
drivers/net/tulip/tulip.h
Copyright 2000,2001 The Linux Kernel Team
Written/copyright 1994-2001 by Donald Becker.
This software may be used and distributed according to the terms
of the GNU General Public License, incorporated herein by reference.
Please refer to Documentation/DocBook/tulip.{pdf,ps,html}
for more information on this driver, or visit the project
Web page at http://sourceforge.net/projects/tulip/
*/
#ifndef __NET_TULIP_H__
#define __NET_TULIP_H__
#include <linux/config.h>
//#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/spinlock.h>
#include <linux/netdevice.h>
#include <linux/timer.h>
#include <linux/delay.h>
#include <asm/io.h>
#include <asm/irq.h>
/* undefine, or define to various debugging levels (>4 == obscene levels) */
#define TULIP_DEBUG 1
/* undefine USE_IO_OPS for MMIO, define for PIO */
#ifdef CONFIG_TULIP_MMIO
# undef USE_IO_OPS
#else
# define USE_IO_OPS 1
#endif
struct tulip_chip_table {
char *chip_name;
int io_size;
int valid_intrs; /* CSR7 interrupt enable settings */
int flags;
void (*media_timer) (unsigned long data);
};
enum tbl_flag {
HAS_MII = 0x0001,
HAS_MEDIA_TABLE = 0x0002,
CSR12_IN_SROM = 0x0004,
ALWAYS_CHECK_MII = 0x0008,
HAS_ACPI = 0x0010,
MC_HASH_ONLY = 0x0020, /* Hash-only multicast filter. */
HAS_PNICNWAY = 0x0080,
HAS_NWAY = 0x0040, /* Uses internal NWay xcvr. */
HAS_INTR_MITIGATION = 0x0100,
IS_ASIX = 0x0200,
HAS_8023X = 0x0400,
COMET_MAC_ADDR = 0x0800,
HAS_PCI_MWI = 0x1000,
};
/* chip types. careful! order is VERY IMPORTANT here, as these
* are used throughout the driver as indices into arrays */
/* Note 21142 == 21143. */
enum chips {
DC21040 = 0,
DC21041 = 1,
DC21140 = 2,
DC21142 = 3, DC21143 = 3,
LC82C168,
MX98713,
MX98715,
MX98725,
AX88140,
PNIC2,
COMET,
COMPEX9881,
I21145,
DM910X,
};
enum MediaIs {
MediaIsFD = 1,
MediaAlwaysFD = 2,
MediaIsMII = 4,
MediaIsFx = 8,
MediaIs100 = 16
};
/* Offsets to the Command and Status Registers, "CSRs". All accesses
must be longword instructions and quadword aligned. */
enum tulip_offsets {
CSR0 = 0,
CSR1 = 0x08,
CSR2 = 0x10,
CSR3 = 0x18,
CSR4 = 0x20,
CSR5 = 0x28,
CSR6 = 0x30,
CSR7 = 0x38,
CSR8 = 0x40,
CSR9 = 0x48,
CSR10 = 0x50,
CSR11 = 0x58,
CSR12 = 0x60,
CSR13 = 0x68,
CSR14 = 0x70,
CSR15 = 0x78,
};
/* register offset and bits for CFDD PCI config reg */
enum pci_cfg_driver_reg {
CFDD = 0x40,
CFDD_Sleep = (1 << 31),
CFDD_Snooze = (1 << 30),
};
/* The bits in the CSR5 status registers, mostly interrupt sources. */
enum status_bits {
TimerInt = 0x800,
SytemError = 0x2000,
TPLnkFail = 0x1000,
TPLnkPass = 0x10,
NormalIntr = 0x10000,
AbnormalIntr = 0x8000,
RxJabber = 0x200,
RxDied = 0x100,
RxNoBuf = 0x80,
RxIntr = 0x40,
TxFIFOUnderflow = 0x20,
TxJabber = 0x08,
TxNoBuf = 0x04,
TxDied = 0x02,
TxIntr = 0x01,
};
enum tulip_mode_bits {
TxThreshold = (1 << 22),
FullDuplex = (1 << 9),
TxOn = 0x2000,
AcceptBroadcast = 0x0100,
AcceptAllMulticast = 0x0080,
AcceptAllPhys = 0x0040,
AcceptRunt = 0x0008,
RxOn = 0x0002,
RxTx = (TxOn | RxOn),
};
enum tulip_busconfig_bits {
MWI = (1 << 24),
MRL = (1 << 23),
MRM = (1 << 21),
CALShift = 14,
BurstLenShift = 8,
};
/* The Tulip Rx and Tx buffer descriptors. */
struct tulip_rx_desc {
s32 status;
s32 length;
u32 buffer1;
u32 buffer2;
};
struct tulip_tx_desc {
s32 status;
s32 length;
u32 buffer1;
u32 buffer2; /* We use only buffer 1. */
};
enum desc_status_bits {
DescOwned = 0x80000000,
RxDescFatalErr = 0x8000,
RxWholePkt = 0x0300,
};
enum t21041_csr13_bits {
csr13_eng = (0xEF0<<4), /* for eng. purposes only, hardcode at EF0h */
csr13_aui = (1<<3), /* clear to force 10bT, set to force AUI/BNC */
csr13_cac = (1<<2), /* CSR13/14/15 autoconfiguration */
csr13_srl = (1<<0), /* When reset, resets all SIA functions, machines */
csr13_mask_auibnc = (csr13_eng | csr13_aui | csr13_srl),
csr13_mask_10bt = (csr13_eng | csr13_srl),
};
enum t21143_csr6_bits {
csr6_sc = (1<<31),
csr6_ra = (1<<30),
csr6_ign_dest_msb = (1<<26),
csr6_mbo = (1<<25),
csr6_scr = (1<<24), /* scramble mode flag: can't be set */
csr6_pcs = (1<<23), /* Enables PCS functions (symbol mode requires csr6_ps be set) default is set */
csr6_ttm = (1<<22), /* Transmit Threshold Mode, set for 10baseT, 0 for 100BaseTX */
csr6_sf = (1<<21), /* Store and forward. If set ignores TR bits */
csr6_hbd = (1<<19), /* Heart beat disable. Disables SQE function in 10baseT */
csr6_ps = (1<<18), /* Port Select. 0 (defualt) = 10baseT, 1 = 100baseTX: can't be set */
csr6_ca = (1<<17), /* Collision Offset Enable. If set uses special algorithm in low collision situations */
csr6_trh = (1<<15), /* Transmit Threshold high bit */
csr6_trl = (1<<14), /* Transmit Threshold low bit */
/***************************************************************
* This table shows transmit threshold values based on media *
* and these two registers (from PNIC1 & 2 docs) Note: this is *
* all meaningless if sf is set. *
***************************************************************/
/***********************************
* (trh,trl) * 100BaseTX * 10BaseT *
***********************************
* (0,0) * 128 * 72 *
* (0,1) * 256 * 96 *
* (1,0) * 512 * 128 *
* (1,1) * 1024 * 160 *
***********************************/
csr6_fc = (1<<12), /* Forces a collision in next transmission (for testing in loopback mode) */
csr6_om_int_loop = (1<<10), /* internal (FIFO) loopback flag */
csr6_om_ext_loop = (1<<11), /* external (PMD) loopback flag */
/* set both and you get (PHY) loopback */
csr6_fd = (1<<9), /* Full duplex mode, disables hearbeat, no loopback */
csr6_pm = (1<<7), /* Pass All Multicast */
csr6_pr = (1<<6), /* Promiscuous mode */
csr6_sb = (1<<5), /* Start(1)/Stop(0) backoff counter */
csr6_if = (1<<4), /* Inverse Filtering, rejects only addresses in address table: can't be set */
csr6_pb = (1<<3), /* Pass Bad Frames, (1) causes even bad frames to be passed on */
csr6_ho = (1<<2), /* Hash-only filtering mode: can't be set */
csr6_hp = (1<<0), /* Hash/Perfect Receive Filtering Mode: can't be set */
csr6_mask_capture = (csr6_sc | csr6_ca),
csr6_mask_defstate = (csr6_mask_capture | csr6_mbo),
csr6_mask_hdcap = (csr6_mask_defstate | csr6_hbd | csr6_ps),
csr6_mask_hdcaptt = (csr6_mask_hdcap | csr6_trh | csr6_trl),
csr6_mask_fullcap = (csr6_mask_hdcaptt | csr6_fd),
csr6_mask_fullpromisc = (csr6_pr | csr6_pm),
csr6_mask_filters = (csr6_hp | csr6_ho | csr6_if),
csr6_mask_100bt = (csr6_scr | csr6_pcs | csr6_hbd),
};
/* Keep the ring sizes a power of two for efficiency.
Making the Tx ring too large decreases the effectiveness of channel
bonding and packet priority.
There are no ill effects from too-large receive rings. */
#undef TX_RING_SIZE
#undef RX_RING_SIZE
#define TX_RING_SIZE 16
#define RX_RING_SIZE 32
#define MEDIA_MASK 31
#define PKT_BUF_SZ 1536 /* Size of each temporary Rx buffer. */
#define TULIP_MIN_CACHE_LINE 8 /* in units of 32-bit words */
#if defined(__sparc__) || defined(__hppa__)
/* The UltraSparc PCI controllers will disconnect at every 64-byte
* crossing anyways so it makes no sense to tell Tulip to burst
* any more than that.
*/
#define TULIP_MAX_CACHE_LINE 16 /* in units of 32-bit words */
#else
#define TULIP_MAX_CACHE_LINE 32 /* in units of 32-bit words */
#endif
/* Ring-wrap flag in length field, use for last ring entry.
0x01000000 means chain on buffer2 address,
0x02000000 means use the ring start address in CSR2/3.
Note: Some work-alike chips do not function correctly in chained mode.
The ASIX chip works only in chained mode.
Thus we indicates ring mode, but always write the 'next' field for
chained mode as well.
*/
#define DESC_RING_WRAP 0x02000000
#define EEPROM_SIZE 128 /* 2 << EEPROM_ADDRLEN */
#define RUN_AT(x) (jiffies + (x))
#if defined(__i386__) /* AKA get_unaligned() */
#define get_u16(ptr) (*(u16 *)(ptr))
#else
#define get_u16(ptr) (((u8*)(ptr))[0] + (((u8*)(ptr))[1]<<8))
#endif
struct medialeaf {
u8 type;
u8 media;
unsigned char *leafdata;
};
struct mediatable {
u16 defaultmedia;
u8 leafcount;
u8 csr12dir; /* General purpose pin directions. */
unsigned has_mii:1;
unsigned has_nonmii:1;
unsigned has_reset:6;
u32 csr15dir;
u32 csr15val; /* 21143 NWay setting. */
struct medialeaf mleaf[0];
};
struct mediainfo {
struct mediainfo *next;
int info_type;
int index;
unsigned char *info;
};
struct ring_info {
struct sk_buff *skb;
dma_addr_t mapping;
};
struct tulip_private {
const char *product_name;
struct net_device *next_module;
struct tulip_rx_desc *rx_ring;
struct tulip_tx_desc *tx_ring;
dma_addr_t rx_ring_dma;
dma_addr_t tx_ring_dma;
/* The saved address of a sent-in-place packet/buffer, for skfree(). */
struct ring_info tx_buffers[TX_RING_SIZE];
/* The addresses of receive-in-place skbuffs. */
struct ring_info rx_buffers[RX_RING_SIZE];
u16 setup_frame[96]; /* Pseudo-Tx frame to init address table. */
int chip_id;
int revision;
int flags;
struct net_device_stats stats;
struct timer_list timer; /* Media selection timer. */
u32 mc_filter[2];
spinlock_t lock;
spinlock_t mii_lock;
unsigned int cur_rx, cur_tx; /* The next free ring entry */
unsigned int dirty_rx, dirty_tx; /* The ring entries to be free()ed. */
#ifdef CONFIG_NET_HW_FLOWCONTROL
#define RX_A_NBF_STOP 0xffffff3f /* To disable RX and RX-NOBUF ints. */
int fc_bit;
int mit_sel;
int mit_change; /* Signal for Interrupt Mitigtion */
#endif
unsigned int full_duplex:1; /* Full-duplex operation requested. */
unsigned int full_duplex_lock:1;
unsigned int fake_addr:1; /* Multiport board faked address. */
unsigned int default_port:4; /* Last dev->if_port value. */
unsigned int media2:4; /* Secondary monitored media port. */
unsigned int medialock:1; /* Don't sense media type. */
unsigned int mediasense:1; /* Media sensing in progress. */
unsigned int nway:1, nwayset:1; /* 21143 internal NWay. */
unsigned int csr0; /* CSR0 setting. */
unsigned int csr6; /* Current CSR6 control settings. */
unsigned char eeprom[EEPROM_SIZE]; /* Serial EEPROM contents. */
void (*link_change) (struct net_device * dev, int csr5);
u16 sym_advertise, mii_advertise; /* NWay capabilities advertised. */
u16 lpar; /* 21143 Link partner ability. */
u16 advertising[4];
signed char phys[4], mii_cnt; /* MII device addresses. */
struct mediatable *mtable;
int cur_index; /* Current media index. */
int saved_if_port;
struct pci_dev *pdev;
int ttimer;
int susp_rx;
unsigned long nir;
unsigned long base_addr;
int pad0, pad1; /* Used for 8-byte alignment */
};
struct eeprom_fixup {
char *name;
unsigned char addr0;
unsigned char addr1;
unsigned char addr2;
u16 newtable[32]; /* Max length below. */
};
/* 21142.c */
extern u16 t21142_csr14[];
void t21142_timer(unsigned long data);
void t21142_start_nway(struct net_device *dev);
void t21142_lnk_change(struct net_device *dev, int csr5);
/* PNIC2.c */
void pnic2_lnk_change(struct net_device *dev, int csr5);
void pnic2_timer(unsigned long data);
void pnic2_start_nway(struct net_device *dev);
void pnic2_lnk_change(struct net_device *dev, int csr5);
/* eeprom.c */
void tulip_parse_eeprom(struct net_device *dev);
int tulip_read_eeprom(long ioaddr, int location, int addr_len);
/* interrupt.c */
extern unsigned int tulip_max_interrupt_work;
extern int tulip_rx_copybreak;
void tulip_interrupt(int irq, void *dev_instance, struct pt_regs *regs);
int tulip_refill_rx(struct net_device *dev);
/* media.c */
int tulip_mdio_read(struct net_device *dev, int phy_id, int location);
void tulip_mdio_write(struct net_device *dev, int phy_id, int location, int value);
void tulip_select_media(struct net_device *dev, int startup);
int tulip_check_duplex(struct net_device *dev);
void tulip_find_mii (struct net_device *dev, int board_idx);
/* pnic.c */
void pnic_do_nway(struct net_device *dev);
void pnic_lnk_change(struct net_device *dev, int csr5);
void pnic_timer(unsigned long data);
/* timer.c */
void tulip_timer(unsigned long data);
void mxic_timer(unsigned long data);
void comet_timer(unsigned long data);
/* tulip_core.c */
extern int tulip_debug;
extern const char * const medianame[];
extern const char tulip_media_cap[];
extern struct tulip_chip_table tulip_tbl[];
extern u8 t21040_csr13[];
extern u16 t21041_csr13[];
extern u16 t21041_csr14[];
extern u16 t21041_csr15[];
#ifndef USE_IO_OPS
#undef inb
#undef inw
#undef inl
#undef outb
#undef outw
#undef outl
#define inb(addr) readb((void*)(addr))
#define inw(addr) readw((void*)(addr))
#define inl(addr) readl((void*)(addr))
#define outb(val,addr) writeb((val), (void*)(addr))
#define outw(val,addr) writew((val), (void*)(addr))
#define outl(val,addr) writel((val), (void*)(addr))
#endif /* !USE_IO_OPS */
static inline void tulip_start_rxtx(struct tulip_private *tp)
{
long ioaddr = tp->base_addr;
outl(tp->csr6 | RxTx, ioaddr + CSR6);
barrier();
(void) inl(ioaddr + CSR6); /* mmio sync */
}
static inline void tulip_stop_rxtx(struct tulip_private *tp)
{
long ioaddr = tp->base_addr;
u32 csr6 = inl(ioaddr + CSR6);
if (csr6 & RxTx) {
outl(csr6 & ~RxTx, ioaddr + CSR6);
barrier();
(void) inl(ioaddr + CSR6); /* mmio sync */
}
}
static inline void tulip_restart_rxtx(struct tulip_private *tp)
{
tulip_stop_rxtx(tp);
udelay(5);
tulip_start_rxtx(tp);
}
#endif /* __NET_TULIP_H__ */
File diff suppressed because it is too large. Load diff
+43
View File
@@ -0,0 +1,43 @@
#
# Makefile for the PCI bus specific drivers.
#
include $(BASEDIR)/Rules.mk
OBJS := pci.o quirks.o compat.o names.o setup-res.o
#obj-$(CONFIG_PCI) += pci.o quirks.o compat.o names.o
#obj-$(CONFIG_PROC_FS) += proc.o
#ifndef CONFIG_SPARC64
#obj-$(CONFIG_PCI) += setup-res.o
#endif
#
# Some architectures use the generic PCI setup functions
#
#obj-$(CONFIG_ALPHA) += setup-bus.o setup-irq.o
#obj-$(CONFIG_ARM) += setup-bus.o setup-irq.o
#obj-$(CONFIG_PARISC) += setup-bus.o
#obj-$(CONFIG_SUPERH) += setup-bus.o setup-irq.o
#obj-$(CONFIG_ALL_PPC) += setup-bus.o
#obj-$(CONFIG_DDB5476) += setup-bus.o
#obj-$(CONFIG_SGI_IP27) += setup-irq.o
#ifndef CONFIG_X86
#obj-y += syscall.o
#endif
default: $(OBJS)
$(LD) -r -o driver.o $(OBJS)
clean:
rm -f *.o *~ core gen-devlist classlist.h devlist.h
names.o: names.c devlist.h classlist.h
devlist.h classlist.h: pci.ids gen-devlist
./gen-devlist <pci.ids
gen-devlist: gen-devlist.c
$(CC) $(CFLAGS) -o gen-devlist gen-devlist.c
+65
View File
@@ -0,0 +1,65 @@
/*
* $Id: compat.c,v 1.1 1998/02/16 10:35:50 mj Exp $
*
* PCI Bus Services -- Function For Backward Compatibility
*
* Copyright 1998--2000 Martin Mares <mj@ucw.cz>
*/
#include <linux/types.h>
//#include <linux/kernel.h>
#include <linux/pci.h>
int
pcibios_present(void)
{
return !list_empty(&pci_devices);
}
int
pcibios_find_class(unsigned int class, unsigned short index, unsigned char *bus, unsigned char *devfn)
{
const struct pci_dev *dev = NULL;
int cnt = 0;
while ((dev = pci_find_class(class, dev)))
if (index == cnt++) {
*bus = dev->bus->number;
*devfn = dev->devfn;
return PCIBIOS_SUCCESSFUL;
}
return PCIBIOS_DEVICE_NOT_FOUND;
}
int
pcibios_find_device(unsigned short vendor, unsigned short device, unsigned short index,
unsigned char *bus, unsigned char *devfn)
{
const struct pci_dev *dev = NULL;
int cnt = 0;
while ((dev = pci_find_device(vendor, device, dev)))
if (index == cnt++) {
*bus = dev->bus->number;
*devfn = dev->devfn;
return PCIBIOS_SUCCESSFUL;
}
return PCIBIOS_DEVICE_NOT_FOUND;
}
#define PCI_OP(rw,size,type) \
int pcibios_##rw##_config_##size (unsigned char bus, unsigned char dev_fn, \
unsigned char where, unsigned type val) \
{ \
struct pci_dev *dev = pci_find_slot(bus, dev_fn); \
if (!dev) return PCIBIOS_DEVICE_NOT_FOUND; \
return pci_##rw##_config_##size(dev, where, val); \
}
PCI_OP(read, byte, char *)
PCI_OP(read, word, short *)
PCI_OP(read, dword, int *)
PCI_OP(write, byte, char)
PCI_OP(write, word, short)
PCI_OP(write, dword, int)
+125
View File
@@ -0,0 +1,125 @@
/*
* Generate devlist.h and classlist.h from the PCI ID file.
*
* (c) 1999--2000 Martin Mares <mj@ucw.cz>
*/
#include <stdio.h>
#include <string.h>
#define MAX_NAME_SIZE 79
static void
pq(FILE *f, const char *c)
{
while (*c) {
if (*c == '"')
fprintf(f, "\\\"");
else
fputc(*c, f);
c++;
}
}
int
main(void)
{
char line[1024], *c, *bra, vend[8];
int vendors = 0;
int mode = 0;
int lino = 0;
int vendor_len = 0;
FILE *devf, *clsf;
devf = fopen("devlist.h", "w");
clsf = fopen("classlist.h", "w");
if (!devf || !clsf) {
fprintf(stderr, "Cannot create output file!\n");
return 1;
}
while (fgets(line, sizeof(line)-1, stdin)) {
lino++;
if ((c = strchr(line, '\n')))
*c = 0;
if (!line[0] || line[0] == '#')
continue;
if (line[1] == ' ') {
if (line[0] == 'C' && strlen(line) > 4 && line[4] == ' ') {
vend[0] = line[2];
vend[1] = line[3];
vend[2] = 0;
mode = 2;
} else goto err;
}
else if (line[0] == '\t') {
if (line[1] == '\t')
continue;
switch (mode) {
case 1:
if (strlen(line) > 5 && line[5] == ' ') {
c = line + 5;
while (*c == ' ')
*c++ = 0;
if (vendor_len + strlen(c) + 1 > MAX_NAME_SIZE) {
/* Too long, try cutting off long description */
bra = strchr(c, '[');
if (bra && bra > c && bra[-1] == ' ')
bra[-1] = 0;
if (vendor_len + strlen(c) + 1 > MAX_NAME_SIZE) {
fprintf(stderr, "Line %d: Device name too long\n", lino);
fprintf(stderr, "%s\n", c);
return 1;
}
}
fprintf(devf, "\tDEVICE(%s,%s,\"", vend, line+1);
pq(devf, c);
fputs("\")\n", devf);
} else goto err;
break;
case 2:
if (strlen(line) > 3 && line[3] == ' ') {
c = line + 3;
while (*c == ' ')
*c++ = 0;
fprintf(clsf, "CLASS(%s%s, \"%s\")\n", vend, line+1, c);
} else goto err;
break;
default:
goto err;
}
} else if (strlen(line) > 4 && line[4] == ' ') {
c = line + 4;
while (*c == ' ')
*c++ = 0;
if (vendors)
fputs("ENDVENDOR()\n\n", devf);
vendors++;
strcpy(vend, line);
vendor_len = strlen(c);
if (vendor_len + 24 > MAX_NAME_SIZE) {
fprintf(stderr, "Line %d: Vendor name too long\n", lino);
return 1;
}
fprintf(devf, "VENDOR(%s,\"", vend);
pq(devf, c);
fputs("\")\n", devf);
mode = 1;
} else {
err:
fprintf(stderr, "Line %d: Syntax error in mode %d: %s\n", lino, mode, line);
return 1;
}
}
fputs("ENDVENDOR()\n\
\n\
#undef VENDOR\n\
#undef DEVICE\n\
#undef ENDVENDOR\n", devf);
fputs("\n#undef CLASS\n", clsf);
fclose(devf);
fclose(clsf);
return 0;
}
+135
View File
@@ -0,0 +1,135 @@
/*
* PCI Class and Device Name Tables
*
* Copyright 1993--1999 Drew Eckhardt, Frederic Potter,
* David Mosberger-Tang, Martin Mares
*/
#include <linux/config.h>
#include <linux/types.h>
//#include <linux/kernel.h>
#include <linux/pci.h>
#include <linux/init.h>
#ifdef CONFIG_PCI_NAMES
struct pci_device_info {
unsigned short device;
unsigned short seen;
const char *name;
};
struct pci_vendor_info {
unsigned short vendor;
unsigned short nr;
const char *name;
struct pci_device_info *devices;
};
/*
* This is ridiculous, but we want the strings in
* the .init section so that they don't take up
* real memory.. Parse the same file multiple times
* to get all the info.
*/
#define VENDOR( vendor, name ) static char __vendorstr_##vendor[] __initdata = name;
#define ENDVENDOR()
#define DEVICE( vendor, device, name ) static char __devicestr_##vendor##device[] __initdata = name;
#include "devlist.h"
#define VENDOR( vendor, name ) static struct pci_device_info __devices_##vendor[] __initdata = {
#define ENDVENDOR() };
#define DEVICE( vendor, device, name ) { 0x##device, 0, __devicestr_##vendor##device },
#include "devlist.h"
static struct pci_vendor_info __initdata pci_vendor_list[] = {
#define VENDOR( vendor, name ) { 0x##vendor, sizeof(__devices_##vendor) / sizeof(struct pci_device_info), __vendorstr_##vendor, __devices_##vendor },
#define ENDVENDOR()
#define DEVICE( vendor, device, name )
#include "devlist.h"
};
#define VENDORS (sizeof(pci_vendor_list)/sizeof(struct pci_vendor_info))
void __devinit pci_name_device(struct pci_dev *dev)
{
const struct pci_vendor_info *vendor_p = pci_vendor_list;
int i = VENDORS;
char *name = dev->name;
do {
if (vendor_p->vendor == dev->vendor)
goto match_vendor;
vendor_p++;
} while (--i);
/* Couldn't find either the vendor nor the device */
sprintf(name, "PCI device %04x:%04x", dev->vendor, dev->device);
return;
match_vendor: {
struct pci_device_info *device_p = vendor_p->devices;
int i = vendor_p->nr;
while (i > 0) {
if (device_p->device == dev->device)
goto match_device;
device_p++;
i--;
}
/* Ok, found the vendor, but unknown device */
sprintf(name, "PCI device %04x:%04x (%s)", dev->vendor, dev->device, vendor_p->name);
return;
/* Full match */
match_device: {
char *n = name + sprintf(name, "%s %s", vendor_p->name, device_p->name);
int nr = device_p->seen + 1;
device_p->seen = nr;
if (nr > 1)
sprintf(n, " (#%d)", nr);
}
}
}
/*
* Class names. Not in .init section as they are needed in runtime.
*/
static u16 pci_class_numbers[] = {
#define CLASS(x,y) 0x##x,
#include "classlist.h"
};
static char *pci_class_names[] = {
#define CLASS(x,y) y,
#include "classlist.h"
};
char *
pci_class_name(u32 class)
{
int i;
for(i=0; i<sizeof(pci_class_numbers)/sizeof(pci_class_numbers[0]); i++)
if (pci_class_numbers[i] == class)
return pci_class_names[i];
return NULL;
}
#else
void __init pci_name_device(struct pci_dev *dev)
{
}
char *
pci_class_name(u32 class)
{
return NULL;
}
#endif /* CONFIG_PCI_NAMES */
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+572
View File
@@ -0,0 +1,572 @@
/*
* $Id: proc.c,v 1.13 1998/05/12 07:36:07 mj Exp $
*
* Procfs interface for the PCI bus.
*
* Copyright (c) 1997--1999 Martin Mares <mj@ucw.cz>
*/
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/pci.h>
#include <linux/proc_fs.h>
#include <linux/init.h>
#include <linux/seq_file.h>
#include <asm/uaccess.h>
#include <asm/byteorder.h>
#define PCI_CFG_SPACE_SIZE 256
static loff_t
proc_bus_pci_lseek(struct file *file, loff_t off, int whence)
{
loff_t new;
switch (whence) {
case 0:
new = off;
break;
case 1:
new = file->f_pos + off;
break;
case 2:
new = PCI_CFG_SPACE_SIZE + off;
break;
default:
return -EINVAL;
}
if (new < 0 || new > PCI_CFG_SPACE_SIZE)
return -EINVAL;
return (file->f_pos = new);
}
static ssize_t
proc_bus_pci_read(struct file *file, char *buf, size_t nbytes, loff_t *ppos)
{
const struct inode *ino = file->f_dentry->d_inode;
const struct proc_dir_entry *dp = ino->u.generic_ip;
struct pci_dev *dev = dp->data;
unsigned int pos = *ppos;
unsigned int cnt, size;
/*
* Normal users can read only the standardized portion of the
* configuration space as several chips lock up when trying to read
* undefined locations (think of Intel PIIX4 as a typical example).
*/
if (capable(CAP_SYS_ADMIN))
size = PCI_CFG_SPACE_SIZE;
else if (dev->hdr_type == PCI_HEADER_TYPE_CARDBUS)
size = 128;
else
size = 64;
if (pos >= size)
return 0;
if (nbytes >= size)
nbytes = size;
if (pos + nbytes > size)
nbytes = size - pos;
cnt = nbytes;
if (!access_ok(VERIFY_WRITE, buf, cnt))
return -EINVAL;
if ((pos & 1) && cnt) {
unsigned char val;
pci_read_config_byte(dev, pos, &val);
__put_user(val, buf);
buf++;
pos++;
cnt--;
}
if ((pos & 3) && cnt > 2) {
unsigned short val;
pci_read_config_word(dev, pos, &val);
__put_user(cpu_to_le16(val), (unsigned short *) buf);
buf += 2;
pos += 2;
cnt -= 2;
}
while (cnt >= 4) {
unsigned int val;
pci_read_config_dword(dev, pos, &val);
__put_user(cpu_to_le32(val), (unsigned int *) buf);
buf += 4;
pos += 4;
cnt -= 4;
}
if (cnt >= 2) {
unsigned short val;
pci_read_config_word(dev, pos, &val);
__put_user(cpu_to_le16(val), (unsigned short *) buf);
buf += 2;
pos += 2;
cnt -= 2;
}
if (cnt) {
unsigned char val;
pci_read_config_byte(dev, pos, &val);
__put_user(val, buf);
buf++;
pos++;
cnt--;
}
*ppos = pos;
return nbytes;
}
static ssize_t
proc_bus_pci_write(struct file *file, const char *buf, size_t nbytes, loff_t *ppos)
{
const struct inode *ino = file->f_dentry->d_inode;
const struct proc_dir_entry *dp = ino->u.generic_ip;
struct pci_dev *dev = dp->data;
int pos = *ppos;
int cnt;
if (pos >= PCI_CFG_SPACE_SIZE)
return 0;
if (nbytes >= PCI_CFG_SPACE_SIZE)
nbytes = PCI_CFG_SPACE_SIZE;
if (pos + nbytes > PCI_CFG_SPACE_SIZE)
nbytes = PCI_CFG_SPACE_SIZE - pos;
cnt = nbytes;
if (!access_ok(VERIFY_READ, buf, cnt))
return -EINVAL;
if ((pos & 1) && cnt) {
unsigned char val;
__get_user(val, buf);
pci_write_config_byte(dev, pos, val);
buf++;
pos++;
cnt--;
}
if ((pos & 3) && cnt > 2) {
unsigned short val;
__get_user(val, (unsigned short *) buf);
pci_write_config_word(dev, pos, le16_to_cpu(val));
buf += 2;
pos += 2;
cnt -= 2;
}
while (cnt >= 4) {
unsigned int val;
__get_user(val, (unsigned int *) buf);
pci_write_config_dword(dev, pos, le32_to_cpu(val));
buf += 4;
pos += 4;
cnt -= 4;
}
if (cnt >= 2) {
unsigned short val;
__get_user(val, (unsigned short *) buf);
pci_write_config_word(dev, pos, le16_to_cpu(val));
buf += 2;
pos += 2;
cnt -= 2;
}
if (cnt) {
unsigned char val;
__get_user(val, buf);
pci_write_config_byte(dev, pos, val);
buf++;
pos++;
cnt--;
}
*ppos = pos;
return nbytes;
}
struct pci_filp_private {
enum pci_mmap_state mmap_state;
int write_combine;
};
static int proc_bus_pci_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
{
const struct proc_dir_entry *dp = inode->u.generic_ip;
struct pci_dev *dev = dp->data;
#ifdef HAVE_PCI_MMAP
struct pci_filp_private *fpriv = file->private_data;
#endif /* HAVE_PCI_MMAP */
int ret = 0;
switch (cmd) {
case PCIIOC_CONTROLLER:
ret = pci_controller_num(dev);
break;
#ifdef HAVE_PCI_MMAP
case PCIIOC_MMAP_IS_IO:
fpriv->mmap_state = pci_mmap_io;
break;
case PCIIOC_MMAP_IS_MEM:
fpriv->mmap_state = pci_mmap_mem;
break;
case PCIIOC_WRITE_COMBINE:
if (arg)
fpriv->write_combine = 1;
else
fpriv->write_combine = 0;
break;
#endif /* HAVE_PCI_MMAP */
default:
ret = -EINVAL;
break;
};
return ret;
}
#ifdef HAVE_PCI_MMAP
static int proc_bus_pci_mmap(struct file *file, struct vm_area_struct *vma)
{
struct inode *inode = file->f_dentry->d_inode;
const struct proc_dir_entry *dp = inode->u.generic_ip;
struct pci_dev *dev = dp->data;
struct pci_filp_private *fpriv = file->private_data;
int ret;
if (!capable(CAP_SYS_RAWIO))
return -EPERM;
ret = pci_mmap_page_range(dev, vma,
fpriv->mmap_state,
fpriv->write_combine);
if (ret < 0)
return ret;
return 0;
}
static int proc_bus_pci_open(struct inode *inode, struct file *file)
{
struct pci_filp_private *fpriv = kmalloc(sizeof(*fpriv), GFP_KERNEL);
if (!fpriv)
return -ENOMEM;
fpriv->mmap_state = pci_mmap_io;
fpriv->write_combine = 0;
file->private_data = fpriv;
return 0;
}
static int proc_bus_pci_release(struct inode *inode, struct file *file)
{
kfree(file->private_data);
file->private_data = NULL;
return 0;
}
#endif /* HAVE_PCI_MMAP */
static struct file_operations proc_bus_pci_operations = {
llseek: proc_bus_pci_lseek,
read: proc_bus_pci_read,
write: proc_bus_pci_write,
ioctl: proc_bus_pci_ioctl,
#ifdef HAVE_PCI_MMAP
open: proc_bus_pci_open,
release: proc_bus_pci_release,
mmap: proc_bus_pci_mmap,
#ifdef HAVE_ARCH_PCI_GET_UNMAPPED_AREA
get_unmapped_area: get_pci_unmapped_area,
#endif /* HAVE_ARCH_PCI_GET_UNMAPPED_AREA */
#endif /* HAVE_PCI_MMAP */
};
#if BITS_PER_LONG == 32
#define LONG_FORMAT "\t%08lx"
#else
#define LONG_FORMAT "\t%16lx"
#endif
/* iterator */
static void *pci_seq_start(struct seq_file *m, loff_t *pos)
{
struct list_head *p = &pci_devices;
loff_t n = *pos;
/* XXX: surely we need some locking for traversing the list? */
while (n--) {
p = p->next;
if (p == &pci_devices)
return NULL;
}
return p;
}
static void *pci_seq_next(struct seq_file *m, void *v, loff_t *pos)
{
struct list_head *p = v;
(*pos)++;
return p->next != &pci_devices ? p->next : NULL;
}
static void pci_seq_stop(struct seq_file *m, void *v)
{
/* release whatever locks we need */
}
static int show_device(struct seq_file *m, void *v)
{
struct list_head *p = v;
const struct pci_dev *dev;
const struct pci_driver *drv;
int i;
if (p == &pci_devices)
return 0;
dev = pci_dev_g(p);
drv = pci_dev_driver(dev);
seq_printf(m, "%02x%02x\t%04x%04x\t%x",
dev->bus->number,
dev->devfn,
dev->vendor,
dev->device,
dev->irq);
/* Here should be 7 and not PCI_NUM_RESOURCES as we need to preserve compatibility */
for(i=0; i<7; i++)
seq_printf(m, LONG_FORMAT,
dev->resource[i].start |
(dev->resource[i].flags & PCI_REGION_FLAG_MASK));
for(i=0; i<7; i++)
seq_printf(m, LONG_FORMAT,
dev->resource[i].start < dev->resource[i].end ?
dev->resource[i].end - dev->resource[i].start + 1 : 0);
seq_putc(m, '\t');
if (drv)
seq_printf(m, "%s", drv->name);
seq_putc(m, '\n');
return 0;
}
static struct seq_operations proc_bus_pci_devices_op = {
start: pci_seq_start,
next: pci_seq_next,
stop: pci_seq_stop,
show: show_device
};
static struct proc_dir_entry *proc_bus_pci_dir;
int pci_proc_attach_device(struct pci_dev *dev)
{
struct pci_bus *bus = dev->bus;
struct proc_dir_entry *de, *e;
char name[16];
if (!(de = bus->procdir)) {
sprintf(name, "%02x", bus->number);
de = bus->procdir = proc_mkdir(name, proc_bus_pci_dir);
if (!de)
return -ENOMEM;
}
sprintf(name, "%02x.%x", PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn));
e = dev->procent = create_proc_entry(name, S_IFREG | S_IRUGO | S_IWUSR, de);
if (!e)
return -ENOMEM;
e->proc_fops = &proc_bus_pci_operations;
e->data = dev;
e->size = PCI_CFG_SPACE_SIZE;
return 0;
}
int pci_proc_detach_device(struct pci_dev *dev)
{
struct proc_dir_entry *e;
if ((e = dev->procent)) {
if (atomic_read(&e->count))
return -EBUSY;
remove_proc_entry(e->name, dev->bus->procdir);
dev->procent = NULL;
}
return 0;
}
int pci_proc_attach_bus(struct pci_bus* bus)
{
struct proc_dir_entry *de = bus->procdir;
if (!de) {
char name[16];
sprintf(name, "%02x", bus->number);
de = bus->procdir = proc_mkdir(name, proc_bus_pci_dir);
if (!de)
return -ENOMEM;
}
return 0;
}
int pci_proc_detach_bus(struct pci_bus* bus)
{
struct proc_dir_entry *de = bus->procdir;
if (de)
remove_proc_entry(de->name, proc_bus_pci_dir);
return 0;
}
/*
* Backward compatible /proc/pci interface.
*/
/*
* Convert some of the configuration space registers of the device at
* address (bus,devfn) into a string (possibly several lines each).
* The configuration string is stored starting at buf[len]. If the
* string would exceed the size of the buffer (SIZE), 0 is returned.
*/
static int show_dev_config(struct seq_file *m, void *v)
{
struct list_head *p = v;
struct pci_dev *dev;
struct pci_driver *drv;
u32 class_rev;
unsigned char latency, min_gnt, max_lat, *class;
int reg;
if (p == &pci_devices) {
seq_puts(m, "PCI devices found:\n");
return 0;
}
dev = pci_dev_g(p);
drv = pci_dev_driver(dev);
pci_read_config_dword(dev, PCI_CLASS_REVISION, &class_rev);
pci_read_config_byte (dev, PCI_LATENCY_TIMER, &latency);
pci_read_config_byte (dev, PCI_MIN_GNT, &min_gnt);
pci_read_config_byte (dev, PCI_MAX_LAT, &max_lat);
seq_printf(m, " Bus %2d, device %3d, function %2d:\n",
dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn));
class = pci_class_name(class_rev >> 16);
if (class)
seq_printf(m, " %s", class);
else
seq_printf(m, " Class %04x", class_rev >> 16);
seq_printf(m, ": %s (rev %d).\n", dev->name, class_rev & 0xff);
if (dev->irq)
seq_printf(m, " IRQ %d.\n", dev->irq);
if (latency || min_gnt || max_lat) {
seq_printf(m, " Master Capable. ");
if (latency)
seq_printf(m, "Latency=%d. ", latency);
else
seq_puts(m, "No bursts. ");
if (min_gnt)
seq_printf(m, "Min Gnt=%d.", min_gnt);
if (max_lat)
seq_printf(m, "Max Lat=%d.", max_lat);
seq_putc(m, '\n');
}
for (reg = 0; reg < 6; reg++) {
struct resource *res = dev->resource + reg;
unsigned long base, end, flags;
base = res->start;
end = res->end;
flags = res->flags;
if (!end)
continue;
if (flags & PCI_BASE_ADDRESS_SPACE_IO) {
seq_printf(m, " I/O at 0x%lx [0x%lx].\n",
base, end);
} else {
const char *pref, *type = "unknown";
if (flags & PCI_BASE_ADDRESS_MEM_PREFETCH)
pref = "P";
else
pref = "Non-p";
switch (flags & PCI_BASE_ADDRESS_MEM_TYPE_MASK) {
case PCI_BASE_ADDRESS_MEM_TYPE_32:
type = "32 bit"; break;
case PCI_BASE_ADDRESS_MEM_TYPE_1M:
type = "20 bit"; break;
case PCI_BASE_ADDRESS_MEM_TYPE_64:
type = "64 bit"; break;
}
seq_printf(m, " %srefetchable %s memory at "
"0x%lx [0x%lx].\n", pref, type,
base,
end);
}
}
return 0;
}
static struct seq_operations proc_pci_op = {
start: pci_seq_start,
next: pci_seq_next,
stop: pci_seq_stop,
show: show_dev_config
};
static int proc_bus_pci_dev_open(struct inode *inode, struct file *file)
{
return seq_open(file, &proc_bus_pci_devices_op);
}
static struct file_operations proc_bus_pci_dev_operations = {
open: proc_bus_pci_dev_open,
read: seq_read,
llseek: seq_lseek,
release: seq_release,
};
static int proc_pci_open(struct inode *inode, struct file *file)
{
return seq_open(file, &proc_pci_op);
}
static struct file_operations proc_pci_operations = {
open: proc_pci_open,
read: seq_read,
llseek: seq_lseek,
release: seq_release,
};
static int __init pci_proc_init(void)
{
if (pci_present()) {
struct proc_dir_entry *entry;
struct pci_dev *dev;
proc_bus_pci_dir = proc_mkdir("pci", proc_bus);
entry = create_proc_entry("devices", 0, proc_bus_pci_dir);
if (entry)
entry->proc_fops = &proc_bus_pci_dev_operations;
pci_for_each_dev(dev) {
pci_proc_attach_device(dev);
}
entry = create_proc_entry("pci", 0, NULL);
if (entry)
entry->proc_fops = &proc_pci_operations;
}
return 0;
}
__initcall(pci_proc_init);
+534
View File
@@ -0,0 +1,534 @@
/*
* $Id: quirks.c,v 1.5 1998/05/02 19:24:14 mj Exp $
*
* This file contains work-arounds for many known PCI hardware
* bugs. Devices present only on certain architectures (host
* bridges et cetera) should be handled in arch-specific code.
*
* Copyright (c) 1999 Martin Mares <mj@ucw.cz>
*
* The bridge optimization stuff has been removed. If you really
* have a silly BIOS which is unable to set your host bridge right,
* use the PowerTweak utility (see http://powertweak.sourceforge.net).
*/
#include <linux/config.h>
#include <linux/types.h>
//#include <linux/kernel.h>
#include <linux/pci.h>
#include <linux/init.h>
#include <linux/delay.h>
#undef DEBUG
/* Deal with broken BIOS'es that neglect to enable passive release,
which can cause problems in combination with the 82441FX/PPro MTRRs */
static void __init quirk_passive_release(struct pci_dev *dev)
{
struct pci_dev *d = NULL;
unsigned char dlc;
/* We have to make sure a particular bit is set in the PIIX3
ISA bridge, so we have to go out and find it. */
while ((d = pci_find_device(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371SB_0, d))) {
pci_read_config_byte(d, 0x82, &dlc);
if (!(dlc & 1<<1)) {
printk(KERN_ERR "PCI: PIIX3: Enabling Passive Release on %s\n", d->slot_name);
dlc |= 1<<1;
pci_write_config_byte(d, 0x82, dlc);
}
}
}
/* The VIA VP2/VP3/MVP3 seem to have some 'features'. There may be a workaround
but VIA don't answer queries. If you happen to have good contacts at VIA
ask them for me please -- Alan
This appears to be BIOS not version dependent. So presumably there is a
chipset level fix */
int isa_dma_bridge_buggy; /* Exported */
static void __init quirk_isa_dma_hangs(struct pci_dev *dev)
{
if (!isa_dma_bridge_buggy) {
isa_dma_bridge_buggy=1;
printk(KERN_INFO "Activating ISA DMA hang workarounds.\n");
}
}
int pci_pci_problems;
/*
* Chipsets where PCI->PCI transfers vanish or hang
*/
static void __init quirk_nopcipci(struct pci_dev *dev)
{
if((pci_pci_problems&PCIPCI_FAIL)==0)
{
printk(KERN_INFO "Disabling direct PCI/PCI transfers.\n");
pci_pci_problems|=PCIPCI_FAIL;
}
}
/*
* Triton requires workarounds to be used by the drivers
*/
static void __init quirk_triton(struct pci_dev *dev)
{
if((pci_pci_problems&PCIPCI_TRITON)==0)
{
printk(KERN_INFO "Limiting direct PCI/PCI transfers.\n");
pci_pci_problems|=PCIPCI_TRITON;
}
}
/*
* VIA Apollo KT133 needs PCI latency patch
* Made according to a windows driver based patch by George E. Breese
* see PCI Latency Adjust on http://www.viahardware.com/download/viatweak.shtm
* Also see http://home.tiscalinet.de/au-ja/review-kt133a-1-en.html for
* the info on which Mr Breese based his work.
*
* Updated based on further information from the site and also on
* information provided by VIA
*/
static void __init quirk_vialatency(struct pci_dev *dev)
{
struct pci_dev *p;
u8 rev;
u8 busarb;
/* Ok we have a potential problem chipset here. Now see if we have
a buggy southbridge */
p=pci_find_device(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C686, NULL);
if(p!=NULL)
{
pci_read_config_byte(p, PCI_CLASS_REVISION, &rev);
/* 0x40 - 0x4f == 686B, 0x10 - 0x2f == 686A; thanks Dan Hollis */
/* Check for buggy part revisions */
if (rev < 0x40 || rev > 0x42)
return;
}
else
{
p = pci_find_device(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8231, NULL);
if(p==NULL) /* No problem parts */
return;
pci_read_config_byte(p, PCI_CLASS_REVISION, &rev);
/* Check for buggy part revisions */
if (rev < 0x10 || rev > 0x12)
return;
}
/*
* Ok we have the problem. Now set the PCI master grant to
* occur every master grant. The apparent bug is that under high
* PCI load (quite common in Linux of course) you can get data
* loss when the CPU is held off the bus for 3 bus master requests
* This happens to include the IDE controllers....
*
* VIA only apply this fix when an SB Live! is present but under
* both Linux and Windows this isnt enough, and we have seen
* corruption without SB Live! but with things like 3 UDMA IDE
* controllers. So we ignore that bit of the VIA recommendation..
*/
pci_read_config_byte(dev, 0x76, &busarb);
/* Set bit 4 and bi 5 of byte 76 to 0x01
"Master priority rotation on every PCI master grant */
busarb &= ~(1<<5);
busarb |= (1<<4);
pci_write_config_byte(dev, 0x76, busarb);
printk(KERN_INFO "Applying VIA southbridge workaround.\n");
}
/*
* VIA Apollo VP3 needs ETBF on BT848/878
*/
static void __init quirk_viaetbf(struct pci_dev *dev)
{
if((pci_pci_problems&PCIPCI_VIAETBF)==0)
{
printk(KERN_INFO "Limiting direct PCI/PCI transfers.\n");
pci_pci_problems|=PCIPCI_VIAETBF;
}
}
static void __init quirk_vsfx(struct pci_dev *dev)
{
if((pci_pci_problems&PCIPCI_VSFX)==0)
{
printk(KERN_INFO "Limiting direct PCI/PCI transfers.\n");
pci_pci_problems|=PCIPCI_VSFX;
}
}
/*
* Natoma has some interesting boundary conditions with Zoran stuff
* at least
*/
static void __init quirk_natoma(struct pci_dev *dev)
{
if((pci_pci_problems&PCIPCI_NATOMA)==0)
{
printk(KERN_INFO "Limiting direct PCI/PCI transfers.\n");
pci_pci_problems|=PCIPCI_NATOMA;
}
}
/*
* S3 868 and 968 chips report region size equal to 32M, but they decode 64M.
* If it's needed, re-allocate the region.
*/
static void __init quirk_s3_64M(struct pci_dev *dev)
{
struct resource *r = &dev->resource[0];
if ((r->start & 0x3ffffff) || r->end != r->start + 0x3ffffff) {
r->start = 0;
r->end = 0x3ffffff;
}
}
static void __init quirk_io_region(struct pci_dev *dev, unsigned region, unsigned size, int nr)
{
region &= ~(size-1);
if (region) {
struct resource *res = dev->resource + nr;
res->name = dev->name;
res->start = region;
res->end = region + size - 1;
res->flags = IORESOURCE_IO;
pci_claim_resource(dev, nr);
}
}
/*
* Let's make the southbridge information explicit instead
* of having to worry about people probing the ACPI areas,
* for example.. (Yes, it happens, and if you read the wrong
* ACPI register it will put the machine to sleep with no
* way of waking it up again. Bummer).
*
* ALI M7101: Two IO regions pointed to by words at
* 0xE0 (64 bytes of ACPI registers)
* 0xE2 (32 bytes of SMB registers)
*/
static void __init quirk_ali7101_acpi(struct pci_dev *dev)
{
u16 region;
pci_read_config_word(dev, 0xE0, &region);
quirk_io_region(dev, region, 64, PCI_BRIDGE_RESOURCES);
pci_read_config_word(dev, 0xE2, &region);
quirk_io_region(dev, region, 32, PCI_BRIDGE_RESOURCES+1);
}
/*
* PIIX4 ACPI: Two IO regions pointed to by longwords at
* 0x40 (64 bytes of ACPI registers)
* 0x90 (32 bytes of SMB registers)
*/
static void __init quirk_piix4_acpi(struct pci_dev *dev)
{
u32 region;
pci_read_config_dword(dev, 0x40, &region);
quirk_io_region(dev, region, 64, PCI_BRIDGE_RESOURCES);
pci_read_config_dword(dev, 0x90, &region);
quirk_io_region(dev, region, 32, PCI_BRIDGE_RESOURCES+1);
}
/*
* VIA ACPI: One IO region pointed to by longword at
* 0x48 or 0x20 (256 bytes of ACPI registers)
*/
static void __init quirk_vt82c586_acpi(struct pci_dev *dev)
{
u8 rev;
u32 region;
pci_read_config_byte(dev, PCI_CLASS_REVISION, &rev);
if (rev & 0x10) {
pci_read_config_dword(dev, 0x48, &region);
region &= PCI_BASE_ADDRESS_IO_MASK;
quirk_io_region(dev, region, 256, PCI_BRIDGE_RESOURCES);
}
}
/*
* VIA VT82C686 ACPI: Three IO region pointed to by (long)words at
* 0x48 (256 bytes of ACPI registers)
* 0x70 (128 bytes of hardware monitoring register)
* 0x90 (16 bytes of SMB registers)
*/
static void __init quirk_vt82c686_acpi(struct pci_dev *dev)
{
u16 hm;
u32 smb;
quirk_vt82c586_acpi(dev);
pci_read_config_word(dev, 0x70, &hm);
hm &= PCI_BASE_ADDRESS_IO_MASK;
quirk_io_region(dev, hm, 128, PCI_BRIDGE_RESOURCES + 1);
pci_read_config_dword(dev, 0x90, &smb);
smb &= PCI_BASE_ADDRESS_IO_MASK;
quirk_io_region(dev, smb, 16, PCI_BRIDGE_RESOURCES + 2);
}
#ifdef CONFIG_X86_IO_APIC
extern int nr_ioapics;
/*
* VIA 686A/B: If an IO-APIC is active, we need to route all on-chip
* devices to the external APIC.
*
* TODO: When we have device-specific interrupt routers,
* this code will go away from quirks.
*/
static void __init quirk_via_ioapic(struct pci_dev *dev)
{
u8 tmp;
if (nr_ioapics < 1)
tmp = 0; /* nothing routed to external APIC */
else
tmp = 0x1f; /* all known bits (4-0) routed to external APIC */
printk(KERN_INFO "PCI: %sbling Via external APIC routing\n",
tmp == 0 ? "Disa" : "Ena");
/* Offset 0x58: External APIC IRQ output control */
pci_write_config_byte (dev, 0x58, tmp);
}
#endif /* CONFIG_X86_IO_APIC */
/*
* Via 686A/B: The PCI_INTERRUPT_LINE register for the on-chip
* devices, USB0/1, AC97, MC97, and ACPI, has an unusual feature:
* when written, it makes an internal connection to the PIC.
* For these devices, this register is defined to be 4 bits wide.
* Normally this is fine. However for IO-APIC motherboards, or
* non-x86 architectures (yes Via exists on PPC among other places),
* we must mask the PCI_INTERRUPT_LINE value versus 0xf to get
* interrupts delivered properly.
*
* TODO: When we have device-specific interrupt routers,
* quirk_via_irqpic will go away from quirks.
*/
/*
* FIXME: it is questionable that quirk_via_acpi
* is needed. It shows up as an ISA bridge, and does not
* support the PCI_INTERRUPT_LINE register at all. Therefore
* it seems like setting the pci_dev's 'irq' to the
* value of the ACPI SCI interrupt is only done for convenience.
* -jgarzik
*/
static void __init quirk_via_acpi(struct pci_dev *d)
{
/*
* VIA ACPI device: SCI IRQ line in PCI config byte 0x42
*/
u8 irq;
pci_read_config_byte(d, 0x42, &irq);
irq &= 0xf;
if (irq && (irq != 2))
d->irq = irq;
}
static void __init quirk_via_irqpic(struct pci_dev *dev)
{
u8 irq, new_irq = dev->irq & 0xf;
pci_read_config_byte(dev, PCI_INTERRUPT_LINE, &irq);
if (new_irq != irq) {
printk(KERN_INFO "PCI: Via IRQ fixup for %s, from %d to %d\n",
dev->slot_name, irq, new_irq);
udelay(15);
pci_write_config_byte(dev, PCI_INTERRUPT_LINE, new_irq);
}
}
/*
* PIIX3 USB: We have to disable USB interrupts that are
* hardwired to PIRQD# and may be shared with an
* external device.
*
* Legacy Support Register (LEGSUP):
* bit13: USB PIRQ Enable (USBPIRQDEN),
* bit4: Trap/SMI On IRQ Enable (USBSMIEN).
*
* We mask out all r/wc bits, too.
*/
static void __init quirk_piix3_usb(struct pci_dev *dev)
{
u16 legsup;
pci_read_config_word(dev, 0xc0, &legsup);
legsup &= 0x50ef;
pci_write_config_word(dev, 0xc0, legsup);
}
/*
* VIA VT82C598 has its device ID settable and many BIOSes
* set it to the ID of VT82C597 for backward compatibility.
* We need to switch it off to be able to recognize the real
* type of the chip.
*/
static void __init quirk_vt82c598_id(struct pci_dev *dev)
{
pci_write_config_byte(dev, 0xfc, 0);
pci_read_config_word(dev, PCI_DEVICE_ID, &dev->device);
}
/*
* CardBus controllers have a legacy base address that enables them
* to respond as i82365 pcmcia controllers. We don't want them to
* do this even if the Linux CardBus driver is not loaded, because
* the Linux i82365 driver does not (and should not) handle CardBus.
*/
static void __init quirk_cardbus_legacy(struct pci_dev *dev)
{
if ((PCI_CLASS_BRIDGE_CARDBUS << 8) ^ dev->class)
return;
pci_write_config_dword(dev, PCI_CB_LEGACY_MODE_BASE, 0);
}
/*
* The AMD io apic can hang the box when an apic irq is masked.
* We check all revs >= B0 (yet not in the pre production!) as the bug
* is currently marked NoFix
*
* We have multiple reports of hangs with this chipset that went away with
* noapic specified. For the moment we assume its the errata. We may be wrong
* of course. However the advice is demonstrably good even if so..
*/
static void __init quirk_amd_ioapic(struct pci_dev *dev)
{
u8 rev;
pci_read_config_byte(dev, PCI_REVISION_ID, &rev);
if(rev >= 0x02)
{
printk(KERN_WARNING "I/O APIC: AMD Errata #22 may be present. In the event of instability try\n");
printk(KERN_WARNING " : booting with the \"noapic\" option.\n");
}
}
/*
* Following the PCI ordering rules is optional on the AMD762. I'm not
* sure what the designers were smoking but let's not inhale...
*
* To be fair to AMD, it follows the spec by default, its BIOS people
* who turn it off!
*/
static void __init quirk_amd_ordering(struct pci_dev *dev)
{
u32 pcic;
pci_read_config_dword(dev, 0x42, &pcic);
if((pcic&2)==0)
{
pcic |= 2;
printk(KERN_WARNING "BIOS disabled PCI ordering compliance, so we enabled it again.\n");
pci_write_config_dword(dev, 0x42, pcic);
}
}
/*
* The main table of quirks.
*/
static struct pci_fixup pci_fixups[] __initdata = {
{ PCI_FIXUP_FINAL, PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82441, quirk_passive_release },
{ PCI_FIXUP_FINAL, PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82441, quirk_passive_release },
/*
* Its not totally clear which chipsets are the problematic ones
* We know 82C586 and 82C596 variants are affected.
*/
{ PCI_FIXUP_FINAL, PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C586_0, quirk_isa_dma_hangs },
{ PCI_FIXUP_FINAL, PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C596, quirk_isa_dma_hangs },
{ PCI_FIXUP_FINAL, PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371SB_0, quirk_isa_dma_hangs },
{ PCI_FIXUP_HEADER, PCI_VENDOR_ID_S3, PCI_DEVICE_ID_S3_868, quirk_s3_64M },
{ PCI_FIXUP_HEADER, PCI_VENDOR_ID_S3, PCI_DEVICE_ID_S3_968, quirk_s3_64M },
{ PCI_FIXUP_FINAL, PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82437, quirk_triton },
{ PCI_FIXUP_FINAL, PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82437VX, quirk_triton },
{ PCI_FIXUP_FINAL, PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82439, quirk_triton },
{ PCI_FIXUP_FINAL, PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82439TX, quirk_triton },
{ PCI_FIXUP_FINAL, PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82441, quirk_natoma },
{ PCI_FIXUP_FINAL, PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82443LX_0, quirk_natoma },
{ PCI_FIXUP_FINAL, PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82443LX_1, quirk_natoma },
{ PCI_FIXUP_FINAL, PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82443BX_0, quirk_natoma },
{ PCI_FIXUP_FINAL, PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82443BX_1, quirk_natoma },
{ PCI_FIXUP_FINAL, PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82443BX_2, quirk_natoma },
{ PCI_FIXUP_FINAL, PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_5597, quirk_nopcipci },
{ PCI_FIXUP_FINAL, PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_496, quirk_nopcipci },
{ PCI_FIXUP_FINAL, PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8363_0, quirk_vialatency },
{ PCI_FIXUP_FINAL, PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8371_1, quirk_vialatency },
{ PCI_FIXUP_FINAL, PCI_VENDOR_ID_VIA, 0x3112 /* Not out yet ? */, quirk_vialatency },
{ PCI_FIXUP_FINAL, PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C576, quirk_vsfx },
{ PCI_FIXUP_FINAL, PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C597_0, quirk_viaetbf },
{ PCI_FIXUP_HEADER, PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C597_0, quirk_vt82c598_id },
{ PCI_FIXUP_HEADER, PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C586_3, quirk_vt82c586_acpi },
{ PCI_FIXUP_HEADER, PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C686_4, quirk_vt82c686_acpi },
{ PCI_FIXUP_HEADER, PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_3, quirk_piix4_acpi },
{ PCI_FIXUP_HEADER, PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M7101, quirk_ali7101_acpi },
{ PCI_FIXUP_HEADER, PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371SB_2, quirk_piix3_usb },
{ PCI_FIXUP_HEADER, PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_2, quirk_piix3_usb },
{ PCI_FIXUP_FINAL, PCI_ANY_ID, PCI_ANY_ID, quirk_cardbus_legacy },
#ifdef CONFIG_X86_IO_APIC
{ PCI_FIXUP_FINAL, PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C686, quirk_via_ioapic },
#endif
{ PCI_FIXUP_HEADER, PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C586_3, quirk_via_acpi },
{ PCI_FIXUP_HEADER, PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C686_4, quirk_via_acpi },
{ PCI_FIXUP_FINAL, PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C586_2, quirk_via_irqpic },
{ PCI_FIXUP_FINAL, PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C686_5, quirk_via_irqpic },
{ PCI_FIXUP_FINAL, PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C686_6, quirk_via_irqpic },
{ PCI_FIXUP_FINAL, PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_VIPER_7410, quirk_amd_ioapic },
{ PCI_FIXUP_FINAL, PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_FE_GATE_700C, quirk_amd_ordering },
{ 0 }
};
static void pci_do_fixups(struct pci_dev *dev, int pass, struct pci_fixup *f)
{
while (f->pass) {
if (f->pass == pass &&
(f->vendor == dev->vendor || f->vendor == (u16) PCI_ANY_ID) &&
(f->device == dev->device || f->device == (u16) PCI_ANY_ID)) {
#ifdef DEBUG
printk(KERN_INFO "PCI: Calling quirk %p for %s\n", f->hook, dev->slot_name);
#endif
f->hook(dev);
}
f++;
}
}
void pci_fixup_device(int pass, struct pci_dev *dev)
{
pci_do_fixups(dev, pass, pcibios_fixups);
pci_do_fixups(dev, pass, pci_fixups);
}
+249
View File
@@ -0,0 +1,249 @@
/*
* drivers/pci/setup-bus.c
*
* Extruded from code written by
* Dave Rusling (david.rusling@reo.mts.dec.com)
* David Mosberger (davidm@cs.arizona.edu)
* David Miller (davem@redhat.com)
*
* Support routines for initializing a PCI subsystem.
*/
/*
* Nov 2000, Ivan Kokshaysky <ink@jurassic.park.msu.ru>
* PCI-PCI bridges cleanup, sorted resource allocation
*/
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/pci.h>
#include <linux/errno.h>
#include <linux/ioport.h>
#include <linux/cache.h>
#include <linux/slab.h>
#define DEBUG_CONFIG 0
#if DEBUG_CONFIG
# define DBGC(args) printk args
#else
# define DBGC(args)
#endif
#define ROUND_UP(x, a) (((x) + (a) - 1) & ~((a) - 1))
static int __init
pbus_assign_resources_sorted(struct pci_bus *bus,
struct pbus_set_ranges_data *ranges)
{
struct list_head *ln;
struct resource *res;
struct resource_list head_io, head_mem, *list, *tmp;
unsigned long io_reserved = 0, mem_reserved = 0;
int idx, found_vga = 0;
head_io.next = head_mem.next = NULL;
for (ln=bus->devices.next; ln != &bus->devices; ln=ln->next) {
struct pci_dev *dev = pci_dev_b(ln);
u16 class = dev->class >> 8;
u16 cmd;
/* First, disable the device to avoid side
effects of possibly overlapping I/O and
memory ranges.
Leave VGA enabled - for obvious reason. :-)
Same with all sorts of bridges - they may
have VGA behind them. */
if (class == PCI_CLASS_DISPLAY_VGA
|| class == PCI_CLASS_NOT_DEFINED_VGA)
found_vga = 1;
else if (class >> 8 != PCI_BASE_CLASS_BRIDGE) {
pci_read_config_word(dev, PCI_COMMAND, &cmd);
cmd &= ~(PCI_COMMAND_IO | PCI_COMMAND_MEMORY
| PCI_COMMAND_MASTER);
pci_write_config_word(dev, PCI_COMMAND, cmd);
}
/* Reserve some resources for CardBus.
Are these values reasonable? */
if (class == PCI_CLASS_BRIDGE_CARDBUS) {
io_reserved += 8*1024;
mem_reserved += 32*1024*1024;
continue;
}
pdev_sort_resources(dev, &head_io, IORESOURCE_IO);
pdev_sort_resources(dev, &head_mem, IORESOURCE_MEM);
}
for (list = head_io.next; list;) {
res = list->res;
idx = res - &list->dev->resource[0];
if (pci_assign_resource(list->dev, idx) == 0
&& ranges->io_end < res->end)
ranges->io_end = res->end;
tmp = list;
list = list->next;
kfree(tmp);
}
for (list = head_mem.next; list;) {
res = list->res;
idx = res - &list->dev->resource[0];
if (pci_assign_resource(list->dev, idx) == 0
&& ranges->mem_end < res->end)
ranges->mem_end = res->end;
tmp = list;
list = list->next;
kfree(tmp);
}
ranges->io_end += io_reserved;
ranges->mem_end += mem_reserved;
/* PCI-to-PCI Bridge Architecture Specification rev. 1.1 (1998)
requires that if there is no I/O ports or memory behind the
bridge, corresponding range must be turned off by writing base
value greater than limit to the bridge's base/limit registers. */
#if 1
/* But assuming that some hardware designed before 1998 might
not support this (very unlikely - at least all DEC bridges
are ok and I believe that was standard de-facto. -ink), we
must allow for at least one unit. */
if (ranges->io_end == ranges->io_start)
ranges->io_end += 1;
if (ranges->mem_end == ranges->mem_start)
ranges->mem_end += 1;
#endif
ranges->io_end = ROUND_UP(ranges->io_end, 4*1024);
ranges->mem_end = ROUND_UP(ranges->mem_end, 1024*1024);
return found_vga;
}
/* Initialize bridges with base/limit values we have collected */
static void __init
pci_setup_bridge(struct pci_bus *bus)
{
struct pbus_set_ranges_data ranges;
struct pci_dev *bridge = bus->self;
u32 l;
if (!bridge || (bridge->class >> 8) != PCI_CLASS_BRIDGE_PCI)
return;
ranges.io_start = bus->resource[0]->start;
ranges.io_end = bus->resource[0]->end;
ranges.mem_start = bus->resource[1]->start;
ranges.mem_end = bus->resource[1]->end;
pcibios_fixup_pbus_ranges(bus, &ranges);
DBGC((KERN_ERR "PCI: Bus %d, bridge: %s\n", bus->number, bridge->name));
DBGC((KERN_ERR " IO window: %04lx-%04lx\n", ranges.io_start, ranges.io_end));
DBGC((KERN_ERR " MEM window: %08lx-%08lx\n", ranges.mem_start, ranges.mem_end));
/* Set up the top and bottom of the PCI I/O segment for this bus. */
pci_read_config_dword(bridge, PCI_IO_BASE, &l);
l &= 0xffff0000;
l |= (ranges.io_start >> 8) & 0x00f0;
l |= ranges.io_end & 0xf000;
pci_write_config_dword(bridge, PCI_IO_BASE, l);
/* Clear upper 16 bits of I/O base/limit. */
pci_write_config_dword(bridge, PCI_IO_BASE_UPPER16, 0);
/* Clear out the upper 32 bits of PREF base/limit. */
pci_write_config_dword(bridge, PCI_PREF_BASE_UPPER32, 0);
pci_write_config_dword(bridge, PCI_PREF_LIMIT_UPPER32, 0);
/* Set up the top and bottom of the PCI Memory segment
for this bus. */
l = (ranges.mem_start >> 16) & 0xfff0;
l |= ranges.mem_end & 0xfff00000;
pci_write_config_dword(bridge, PCI_MEMORY_BASE, l);
/* Set up PREF base/limit. */
l = (bus->resource[2]->start >> 16) & 0xfff0;
l |= bus->resource[2]->end & 0xfff00000;
pci_write_config_dword(bridge, PCI_PREF_MEMORY_BASE, l);
/* Check if we have VGA behind the bridge.
Enable ISA in either case. */
l = (bus->resource[0]->flags & IORESOURCE_BUS_HAS_VGA) ? 0x0c : 0x04;
pci_write_config_word(bridge, PCI_BRIDGE_CONTROL, l);
}
static void __init
pbus_assign_resources(struct pci_bus *bus, struct pbus_set_ranges_data *ranges)
{
struct list_head *ln;
int found_vga = pbus_assign_resources_sorted(bus, ranges);
if (!ranges->found_vga && found_vga) {
struct pci_bus *b;
ranges->found_vga = 1;
/* Propogate presence of the VGA to upstream bridges */
for (b = bus; b->parent; b = b->parent) {
#if 0
/* ? Do we actually need to enable PF memory? */
b->resource[2]->start = 0;
#endif
b->resource[0]->flags |= IORESOURCE_BUS_HAS_VGA;
}
}
for (ln=bus->children.next; ln != &bus->children; ln=ln->next) {
struct pci_bus *b = pci_bus_b(ln);
b->resource[0]->start = ranges->io_start = ranges->io_end;
b->resource[1]->start = ranges->mem_start = ranges->mem_end;
pbus_assign_resources(b, ranges);
b->resource[0]->end = ranges->io_end - 1;
b->resource[1]->end = ranges->mem_end - 1;
pci_setup_bridge(b);
}
}
void __init
pci_assign_unassigned_resources(void)
{
struct pbus_set_ranges_data ranges;
struct list_head *ln;
struct pci_dev *dev;
for(ln=pci_root_buses.next; ln != &pci_root_buses; ln=ln->next) {
struct pci_bus *b = pci_bus_b(ln);
ranges.io_start = b->resource[0]->start + PCIBIOS_MIN_IO;
ranges.mem_start = b->resource[1]->start + PCIBIOS_MIN_MEM;
ranges.io_end = ranges.io_start;
ranges.mem_end = ranges.mem_start;
ranges.found_vga = 0;
pbus_assign_resources(b, &ranges);
}
pci_for_each_dev(dev) {
pdev_enable_device(dev);
}
}
/* Check whether the bridge supports I/O forwarding.
If not, its I/O base/limit register must be
read-only and read as 0. */
unsigned long __init
pci_bridge_check_io(struct pci_dev *bridge)
{
u16 io;
pci_read_config_word(bridge, PCI_IO_BASE, &io);
if (!io) {
pci_write_config_word(bridge, PCI_IO_BASE, 0xf0f0);
pci_read_config_word(bridge, PCI_IO_BASE, &io);
pci_write_config_word(bridge, PCI_IO_BASE, 0x0);
}
if (io)
return IORESOURCE_IO;
printk(KERN_WARNING "PCI: bridge %s does not support I/O forwarding!\n",
bridge->name);
return 0;
}
+71
View File
@@ -0,0 +1,71 @@
/*
* drivers/pci/setup-irq.c
*
* Extruded from code written by
* Dave Rusling (david.rusling@reo.mts.dec.com)
* David Mosberger (davidm@cs.arizona.edu)
* David Miller (davem@redhat.com)
*
* Support routines for initializing a PCI subsystem.
*/
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/pci.h>
#include <linux/errno.h>
#include <linux/ioport.h>
#include <linux/cache.h>
#define DEBUG_CONFIG 0
#if DEBUG_CONFIG
# define DBGC(args) printk args
#else
# define DBGC(args)
#endif
static void __init
pdev_fixup_irq(struct pci_dev *dev,
u8 (*swizzle)(struct pci_dev *, u8 *),
int (*map_irq)(struct pci_dev *, u8, u8))
{
u8 pin, slot;
int irq;
/* If this device is not on the primary bus, we need to figure out
which interrupt pin it will come in on. We know which slot it
will come in on 'cos that slot is where the bridge is. Each
time the interrupt line passes through a PCI-PCI bridge we must
apply the swizzle function. */
pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin);
/* Cope with 0 and illegal. */
if (pin == 0 || pin > 4)
pin = 1;
/* Follow the chain of bridges, swizzling as we go. */
slot = (*swizzle)(dev, &pin);
irq = (*map_irq)(dev, slot, pin);
if (irq == -1)
irq = 0;
dev->irq = irq;
DBGC((KERN_ERR "PCI fixup irq: (%s) got %d\n", dev->name, dev->irq));
/* Always tell the device, so the driver knows what is
the real IRQ to use; the device does not use it. */
pcibios_update_irq(dev, irq);
}
void __init
pci_fixup_irqs(u8 (*swizzle)(struct pci_dev *, u8 *),
int (*map_irq)(struct pci_dev *, u8, u8))
{
struct pci_dev *dev;
pci_for_each_dev(dev) {
pdev_fixup_irq(dev, swizzle, map_irq);
}
}
+234
View File
@@ -0,0 +1,234 @@
/*
* drivers/pci/setup-res.c
*
* Extruded from code written by
* Dave Rusling (david.rusling@reo.mts.dec.com)
* David Mosberger (davidm@cs.arizona.edu)
* David Miller (davem@redhat.com)
*
* Support routines for initializing a PCI subsystem.
*/
/* fixed for multiple pci buses, 1999 Andrea Arcangeli <andrea@suse.de> */
/*
* Nov 2000, Ivan Kokshaysky <ink@jurassic.park.msu.ru>
* Resource sorting
*/
#include <linux/init.h>
//#include <linux/kernel.h>
#include <linux/pci.h>
#include <linux/errno.h>
#include <linux/ioport.h>
#include <linux/cache.h>
#include <linux/slab.h>
#define DEBUG_CONFIG 0
#if DEBUG_CONFIG
# define DBGC(args) printk args
#else
# define DBGC(args)
#endif
int __init
pci_claim_resource(struct pci_dev *dev, int resource)
{
struct resource *res = &dev->resource[resource];
struct resource *root = pci_find_parent_resource(dev, res);
int err;
err = -EINVAL;
if (root != NULL) {
err = request_resource(root, res);
if (err) {
printk(KERN_ERR "PCI: Address space collision on "
"region %d of device %s [%lx:%lx]\n",
resource, dev->name, res->start, res->end);
}
} else {
printk(KERN_ERR "PCI: No parent found for region %d "
"of device %s\n", resource, dev->name);
}
return err;
}
/*
* Given the PCI bus a device resides on, try to
* find an acceptable resource allocation for a
* specific device resource..
*/
static int pci_assign_bus_resource(const struct pci_bus *bus,
struct pci_dev *dev,
struct resource *res,
unsigned long size,
unsigned long min,
unsigned int type_mask,
int resno)
{
int i;
type_mask |= IORESOURCE_IO | IORESOURCE_MEM;
for (i = 0 ; i < 4; i++) {
struct resource *r = bus->resource[i];
if (!r)
continue;
/* type_mask must match */
if ((res->flags ^ r->flags) & type_mask)
continue;
/* We cannot allocate a non-prefetching resource from a pre-fetching area */
if ((r->flags & IORESOURCE_PREFETCH) && !(res->flags & IORESOURCE_PREFETCH))
continue;
/* Ok, try it out.. */
if (allocate_resource(r, res, size, min, -1, size, pcibios_align_resource, dev) < 0)
continue;
/* Update PCI config space. */
pcibios_update_resource(dev, r, res, resno);
return 0;
}
return -EBUSY;
}
int
pci_assign_resource(struct pci_dev *dev, int i)
{
const struct pci_bus *bus = dev->bus;
struct resource *res = dev->resource + i;
unsigned long size, min;
size = res->end - res->start + 1;
min = (res->flags & IORESOURCE_IO) ? PCIBIOS_MIN_IO : PCIBIOS_MIN_MEM;
/* First, try exact prefetching match.. */
if (pci_assign_bus_resource(bus, dev, res, size, min, IORESOURCE_PREFETCH, i) < 0) {
/*
* That failed.
*
* But a prefetching area can handle a non-prefetching
* window (it will just not perform as well).
*/
if (!(res->flags & IORESOURCE_PREFETCH) || pci_assign_bus_resource(bus, dev, res, size, min, 0, i) < 0) {
printk(KERN_ERR "PCI: Failed to allocate resource %d(%lx-%lx) for %s\n",
i, res->start, res->end, dev->slot_name);
return -EBUSY;
}
}
DBGC((KERN_ERR " got res[%lx:%lx] for resource %d of %s\n", res->start,
res->end, i, dev->name));
return 0;
}
/* Sort resources of a given type by alignment */
void __init
pdev_sort_resources(struct pci_dev *dev,
struct resource_list *head, u32 type_mask)
{
int i;
for (i = 0; i < PCI_NUM_RESOURCES; i++) {
struct resource *r;
struct resource_list *list, *tmp;
unsigned long r_size;
/* PCI-PCI bridges may have I/O ports or
memory on the primary bus */
if (dev->class >> 8 == PCI_CLASS_BRIDGE_PCI &&
i >= PCI_BRIDGE_RESOURCES)
continue;
r = &dev->resource[i];
r_size = r->end - r->start;
if (!(r->flags & type_mask) || r->parent)
continue;
if (!r_size) {
printk(KERN_WARNING "PCI: Ignore bogus resource %d "
"[%lx:%lx] of %s\n",
i, r->start, r->end, dev->name);
continue;
}
for (list = head; ; list = list->next) {
unsigned long size = 0;
struct resource_list *ln = list->next;
if (ln)
size = ln->res->end - ln->res->start;
if (r_size > size) {
tmp = kmalloc(sizeof(*tmp), GFP_KERNEL);
if (!tmp) {
printk(KERN_ERR "pdev_sort_resources(): kmalloc() failed!\n");
continue;
}
tmp->next = ln;
tmp->res = r;
tmp->dev = dev;
list->next = tmp;
break;
}
}
}
}
void __init
pdev_enable_device(struct pci_dev *dev)
{
u32 reg;
u16 cmd;
int i;
DBGC((KERN_ERR "PCI enable device: (%s)\n", dev->name));
pci_read_config_word(dev, PCI_COMMAND, &cmd);
for (i = 0; i < PCI_NUM_RESOURCES; i++) {
struct resource *res = &dev->resource[i];
if (res->flags & IORESOURCE_IO)
cmd |= PCI_COMMAND_IO;
else if (res->flags & IORESOURCE_MEM)
cmd |= PCI_COMMAND_MEMORY;
}
/* Special case, disable the ROM. Several devices act funny
(ie. do not respond to memory space writes) when it is left
enabled. A good example are QlogicISP adapters. */
if (dev->rom_base_reg) {
pci_read_config_dword(dev, dev->rom_base_reg, &reg);
reg &= ~PCI_ROM_ADDRESS_ENABLE;
pci_write_config_dword(dev, dev->rom_base_reg, reg);
dev->resource[PCI_ROM_RESOURCE].flags &= ~PCI_ROM_ADDRESS_ENABLE;
}
/* All of these (may) have I/O scattered all around and may not
use I/O base address registers at all. So we just have to
always enable IO to these devices. */
if ((dev->class >> 8) == PCI_CLASS_NOT_DEFINED
|| (dev->class >> 8) == PCI_CLASS_NOT_DEFINED_VGA
|| (dev->class >> 8) == PCI_CLASS_STORAGE_IDE
|| (dev->class >> 16) == PCI_BASE_CLASS_DISPLAY) {
cmd |= PCI_COMMAND_IO;
}
/* ??? Always turn on bus mastering. If the device doesn't support
it, the bit will go into the bucket. */
cmd |= PCI_COMMAND_MASTER;
/* Set the cache line and default latency (32). */
pci_write_config_word(dev, PCI_CACHE_LINE_SIZE,
(32 << 8) | (L1_CACHE_BYTES / sizeof(u32)));
/* Enable the appropriate bits in the PCI command register. */
pci_write_config_word(dev, PCI_COMMAND, cmd);
DBGC((KERN_ERR " cmd reg 0x%x\n", cmd));
}
Loaded 100 of 401 files, more files were not shown because too many files have changed in this diff. Show more
Reference in new issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.