agp.c revision 238172
161452Sdfr/*- 261452Sdfr * Copyright (c) 2000 Doug Rabson 361452Sdfr * All rights reserved. 461452Sdfr * 561452Sdfr * Redistribution and use in source and binary forms, with or without 661452Sdfr * modification, are permitted provided that the following conditions 761452Sdfr * are met: 861452Sdfr * 1. Redistributions of source code must retain the above copyright 961452Sdfr * notice, this list of conditions and the following disclaimer. 1061452Sdfr * 2. Redistributions in binary form must reproduce the above copyright 1161452Sdfr * notice, this list of conditions and the following disclaimer in the 1261452Sdfr * documentation and/or other materials provided with the distribution. 1361452Sdfr * 1461452Sdfr * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1561452Sdfr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1661452Sdfr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1761452Sdfr * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1861452Sdfr * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1961452Sdfr * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2061452Sdfr * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2161452Sdfr * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2261452Sdfr * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2361452Sdfr * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2461452Sdfr * SUCH DAMAGE. 2561452Sdfr */ 2661452Sdfr 27116192Sobrien#include <sys/cdefs.h> 28116192Sobrien__FBSDID("$FreeBSD: head/sys/dev/agp/agp.c 238172 2012-07-06 15:57:03Z marcel $"); 29116192Sobrien 30188247Swkoszek#include "opt_agp.h" 3161452Sdfr#include "opt_bus.h" 3261452Sdfr 3361452Sdfr#include <sys/param.h> 3461452Sdfr#include <sys/systm.h> 3561452Sdfr#include <sys/malloc.h> 3661452Sdfr#include <sys/kernel.h> 37129878Sphk#include <sys/module.h> 3861452Sdfr#include <sys/bus.h> 3961452Sdfr#include <sys/conf.h> 4061452Sdfr#include <sys/ioccom.h> 4161452Sdfr#include <sys/agpio.h> 4261452Sdfr#include <sys/lock.h> 4376827Salfred#include <sys/mutex.h> 4469927Sjhb#include <sys/proc.h> 4561452Sdfr 46173573Sjhb#include <dev/agp/agppriv.h> 47173573Sjhb#include <dev/agp/agpvar.h> 48173573Sjhb#include <dev/agp/agpreg.h> 49119288Simp#include <dev/pci/pcivar.h> 50119288Simp#include <dev/pci/pcireg.h> 5161452Sdfr 5261452Sdfr#include <vm/vm.h> 5361452Sdfr#include <vm/vm_object.h> 5461452Sdfr#include <vm/vm_page.h> 5561452Sdfr#include <vm/vm_pageout.h> 5661452Sdfr#include <vm/pmap.h> 5761452Sdfr 5861452Sdfr#include <machine/bus.h> 5961452Sdfr#include <machine/resource.h> 6061452Sdfr#include <sys/rman.h> 6161452Sdfr 6261452SdfrMODULE_VERSION(agp, 1); 6361452Sdfr 6470185SassarMALLOC_DEFINE(M_AGP, "agp", "AGP data structures"); 6561452Sdfr 6661452Sdfr /* agp_drv.c */ 6761452Sdfrstatic d_open_t agp_open; 6861452Sdfrstatic d_close_t agp_close; 6961452Sdfrstatic d_ioctl_t agp_ioctl; 7061452Sdfrstatic d_mmap_t agp_mmap; 7161452Sdfr 7261452Sdfrstatic struct cdevsw agp_cdevsw = { 73126080Sphk .d_version = D_VERSION, 74126080Sphk .d_flags = D_NEEDGIANT, 75111815Sphk .d_open = agp_open, 76111815Sphk .d_close = agp_close, 77111815Sphk .d_ioctl = agp_ioctl, 78111815Sphk .d_mmap = agp_mmap, 79111815Sphk .d_name = "agp", 8061452Sdfr}; 8161452Sdfr 8261452Sdfrstatic devclass_t agp_devclass; 8361452Sdfr 8461452Sdfr/* Helper functions for implementing chipset mini drivers. */ 8561452Sdfr 8661501Sdfrvoid 8761501Sdfragp_flush_cache() 8861501Sdfr{ 89133852Sobrien#if defined(__i386__) || defined(__amd64__) 9061501Sdfr wbinvd(); 9161452Sdfr#endif 9261501Sdfr} 9361452Sdfr 9461452Sdfru_int8_t 9561452Sdfragp_find_caps(device_t dev) 9661452Sdfr{ 97153561Sjhb int capreg; 9861452Sdfr 9961452Sdfr 100219902Sjhb if (pci_find_cap(dev, PCIY_AGP, &capreg) != 0) 101153561Sjhb capreg = 0; 102153561Sjhb return (capreg); 10361452Sdfr} 10461452Sdfr 10561452Sdfr/* 10661452Sdfr * Find an AGP display device (if any). 10761452Sdfr */ 10861452Sdfrstatic device_t 10961452Sdfragp_find_display(void) 11061452Sdfr{ 11161452Sdfr devclass_t pci = devclass_find("pci"); 11261452Sdfr device_t bus, dev = 0; 11361452Sdfr device_t *kids; 11461452Sdfr int busnum, numkids, i; 11561452Sdfr 11661452Sdfr for (busnum = 0; busnum < devclass_get_maxunit(pci); busnum++) { 11761452Sdfr bus = devclass_get_device(pci, busnum); 11861452Sdfr if (!bus) 11961452Sdfr continue; 120182068Simp if (device_get_children(bus, &kids, &numkids) != 0) 121182068Simp continue; 12261452Sdfr for (i = 0; i < numkids; i++) { 12361452Sdfr dev = kids[i]; 12461452Sdfr if (pci_get_class(dev) == PCIC_DISPLAY 12561452Sdfr && pci_get_subclass(dev) == PCIS_DISPLAY_VGA) 12661452Sdfr if (agp_find_caps(dev)) { 12761452Sdfr free(kids, M_TEMP); 12861452Sdfr return dev; 12961452Sdfr } 13061452Sdfr 13161452Sdfr } 13261452Sdfr free(kids, M_TEMP); 13361452Sdfr } 13461452Sdfr 13561452Sdfr return 0; 13661452Sdfr} 13761452Sdfr 13861452Sdfrstruct agp_gatt * 13961452Sdfragp_alloc_gatt(device_t dev) 14061452Sdfr{ 14161452Sdfr u_int32_t apsize = AGP_GET_APERTURE(dev); 14261452Sdfr u_int32_t entries = apsize >> AGP_PAGE_SHIFT; 14361452Sdfr struct agp_gatt *gatt; 14461452Sdfr 14561452Sdfr if (bootverbose) 14661452Sdfr device_printf(dev, 14761452Sdfr "allocating GATT for aperture of size %dM\n", 14861452Sdfr apsize / (1024*1024)); 14961452Sdfr 150122513Sanholt if (entries == 0) { 151122513Sanholt device_printf(dev, "bad aperture size\n"); 152122513Sanholt return NULL; 153122513Sanholt } 154122513Sanholt 15561452Sdfr gatt = malloc(sizeof(struct agp_gatt), M_AGP, M_NOWAIT); 15661452Sdfr if (!gatt) 15761452Sdfr return 0; 15861452Sdfr 15961452Sdfr gatt->ag_entries = entries; 16061452Sdfr gatt->ag_virtual = contigmalloc(entries * sizeof(u_int32_t), M_AGP, 0, 16161452Sdfr 0, ~0, PAGE_SIZE, 0); 16261452Sdfr if (!gatt->ag_virtual) { 16361452Sdfr if (bootverbose) 16461452Sdfr device_printf(dev, "contiguous allocation failed\n"); 16561452Sdfr free(gatt, M_AGP); 16661452Sdfr return 0; 16761452Sdfr } 16861452Sdfr bzero(gatt->ag_virtual, entries * sizeof(u_int32_t)); 16961452Sdfr gatt->ag_physical = vtophys((vm_offset_t) gatt->ag_virtual); 17061452Sdfr agp_flush_cache(); 17161452Sdfr 17261452Sdfr return gatt; 17361452Sdfr} 17461452Sdfr 17561452Sdfrvoid 17661452Sdfragp_free_gatt(struct agp_gatt *gatt) 17761452Sdfr{ 17861452Sdfr contigfree(gatt->ag_virtual, 17961452Sdfr gatt->ag_entries * sizeof(u_int32_t), M_AGP); 18061452Sdfr free(gatt, M_AGP); 18161452Sdfr} 18261452Sdfr 183163362Stanimurastatic u_int agp_max[][2] = { 18461452Sdfr {0, 0}, 18561452Sdfr {32, 4}, 18661452Sdfr {64, 28}, 18761452Sdfr {128, 96}, 18861452Sdfr {256, 204}, 18961452Sdfr {512, 440}, 19061452Sdfr {1024, 942}, 19161452Sdfr {2048, 1920}, 19261452Sdfr {4096, 3932} 19361452Sdfr}; 19461452Sdfr#define agp_max_size (sizeof(agp_max) / sizeof(agp_max[0])) 19561452Sdfr 196171433Sanholt/** 197171433Sanholt * Sets the PCI resource which represents the AGP aperture. 198171433Sanholt * 199171433Sanholt * If not called, the default AGP aperture resource of AGP_APBASE will 200171433Sanholt * be used. Must be called before agp_generic_attach(). 201171433Sanholt */ 202171433Sanholtvoid 203171433Sanholtagp_set_aperture_resource(device_t dev, int rid) 204171433Sanholt{ 205171433Sanholt struct agp_softc *sc = device_get_softc(dev); 206171433Sanholt 207171433Sanholt sc->as_aperture_rid = rid; 208171433Sanholt} 209171433Sanholt 21061452Sdfrint 21161452Sdfragp_generic_attach(device_t dev) 21261452Sdfr{ 21361452Sdfr struct agp_softc *sc = device_get_softc(dev); 214171433Sanholt int i; 215163362Stanimura u_int memsize; 21661452Sdfr 21761452Sdfr /* 218171433Sanholt * Find and map the aperture, RF_SHAREABLE for DRM but not RF_ACTIVE 219171433Sanholt * because the kernel doesn't need to map it. 22061452Sdfr */ 221171433Sanholt 222214603Snwhitehorn if (sc->as_aperture_rid != -1) { 223214603Snwhitehorn if (sc->as_aperture_rid == 0) 224214603Snwhitehorn sc->as_aperture_rid = AGP_APBASE; 22561452Sdfr 226214603Snwhitehorn sc->as_aperture = bus_alloc_resource_any(dev, SYS_RES_MEMORY, 227214603Snwhitehorn &sc->as_aperture_rid, RF_SHAREABLE); 228214603Snwhitehorn if (!sc->as_aperture) 229214603Snwhitehorn return ENOMEM; 230214603Snwhitehorn } 231214603Snwhitehorn 23261452Sdfr /* 23361452Sdfr * Work out an upper bound for agp memory allocation. This 23461452Sdfr * uses a heurisitc table from the Linux driver. 23561452Sdfr */ 236238172Smarcel memsize = ptoa(realmem) >> 20; 23761452Sdfr for (i = 0; i < agp_max_size; i++) { 23861452Sdfr if (memsize <= agp_max[i][0]) 23961452Sdfr break; 24061452Sdfr } 241235782Skib if (i == agp_max_size) 242235782Skib i = agp_max_size - 1; 24361452Sdfr sc->as_maxmem = agp_max[i][1] << 20U; 24461452Sdfr 24561452Sdfr /* 24661452Sdfr * The lock is used to prevent re-entry to 24761452Sdfr * agp_generic_bind_memory() since that function can sleep. 24861452Sdfr */ 249129579Smux mtx_init(&sc->as_lock, "agp lock", NULL, MTX_DEF); 25061452Sdfr 25161452Sdfr /* 25261452Sdfr * Initialise stuff for the userland device. 25361452Sdfr */ 25461452Sdfr agp_devclass = devclass_find("agp"); 25561452Sdfr TAILQ_INIT(&sc->as_memory); 25661452Sdfr sc->as_nextid = 1; 25761452Sdfr 25861452Sdfr sc->as_devnode = make_dev(&agp_cdevsw, 259191057Sed 0, UID_ROOT, GID_WHEEL, 0600, "agpgart"); 260191057Sed sc->as_devnode->si_drv1 = dev; 26161452Sdfr 26261452Sdfr return 0; 26361452Sdfr} 26461452Sdfr 265173203Sjhbvoid 266173203Sjhbagp_free_cdev(device_t dev) 26761452Sdfr{ 26861452Sdfr struct agp_softc *sc = device_get_softc(dev); 269153562Sjhb 270153562Sjhb destroy_dev(sc->as_devnode); 271173203Sjhb} 272173203Sjhb 273173203Sjhbvoid 274173203Sjhbagp_free_res(device_t dev) 275173203Sjhb{ 276173203Sjhb struct agp_softc *sc = device_get_softc(dev); 277173203Sjhb 278214603Snwhitehorn if (sc->as_aperture != NULL) 279214603Snwhitehorn bus_release_resource(dev, SYS_RES_MEMORY, sc->as_aperture_rid, 280214603Snwhitehorn sc->as_aperture); 281129579Smux mtx_destroy(&sc->as_lock); 28261452Sdfr agp_flush_cache(); 283173203Sjhb} 284173203Sjhb 285173203Sjhbint 286173203Sjhbagp_generic_detach(device_t dev) 287173203Sjhb{ 288173203Sjhb 289173203Sjhb agp_free_cdev(dev); 290173203Sjhb agp_free_res(dev); 29161452Sdfr return 0; 29261452Sdfr} 29361452Sdfr 294171433Sanholt/** 295171433Sanholt * Default AGP aperture size detection which simply returns the size of 296171433Sanholt * the aperture's PCI resource. 297171433Sanholt */ 298189578Simpu_int32_t 299171433Sanholtagp_generic_get_aperture(device_t dev) 300171433Sanholt{ 301171433Sanholt struct agp_softc *sc = device_get_softc(dev); 302171433Sanholt 303171433Sanholt return rman_get_size(sc->as_aperture); 304171433Sanholt} 305171433Sanholt 306171433Sanholt/** 307171433Sanholt * Default AGP aperture size setting function, which simply doesn't allow 308171433Sanholt * changes to resource size. 309171433Sanholt */ 310171433Sanholtint 311171433Sanholtagp_generic_set_aperture(device_t dev, u_int32_t aperture) 312171433Sanholt{ 313171433Sanholt u_int32_t current_aperture; 314171433Sanholt 315171433Sanholt current_aperture = AGP_GET_APERTURE(dev); 316171433Sanholt if (current_aperture != aperture) 317171433Sanholt return EINVAL; 318171433Sanholt else 319171433Sanholt return 0; 320171433Sanholt} 321171433Sanholt 322121440Sjhb/* 323121440Sjhb * This does the enable logic for v3, with the same topology 324121440Sjhb * restrictions as in place for v2 -- one bus, one device on the bus. 325121440Sjhb */ 326121440Sjhbstatic int 327121440Sjhbagp_v3_enable(device_t dev, device_t mdev, u_int32_t mode) 32861452Sdfr{ 32961452Sdfr u_int32_t tstatus, mstatus; 33061452Sdfr u_int32_t command; 331121440Sjhb int rq, sba, fw, rate, arqsz, cal; 33261452Sdfr 333121440Sjhb tstatus = pci_read_config(dev, agp_find_caps(dev) + AGP_STATUS, 4); 334121440Sjhb mstatus = pci_read_config(mdev, agp_find_caps(mdev) + AGP_STATUS, 4); 33561452Sdfr 336121440Sjhb /* Set RQ to the min of mode, tstatus and mstatus */ 337121440Sjhb rq = AGP_MODE_GET_RQ(mode); 338121440Sjhb if (AGP_MODE_GET_RQ(tstatus) < rq) 339121440Sjhb rq = AGP_MODE_GET_RQ(tstatus); 340121440Sjhb if (AGP_MODE_GET_RQ(mstatus) < rq) 341121440Sjhb rq = AGP_MODE_GET_RQ(mstatus); 342121440Sjhb 343121440Sjhb /* 344121440Sjhb * ARQSZ - Set the value to the maximum one. 345121440Sjhb * Don't allow the mode register to override values. 346121440Sjhb */ 347121440Sjhb arqsz = AGP_MODE_GET_ARQSZ(mode); 348121440Sjhb if (AGP_MODE_GET_ARQSZ(tstatus) > rq) 349121440Sjhb rq = AGP_MODE_GET_ARQSZ(tstatus); 350121440Sjhb if (AGP_MODE_GET_ARQSZ(mstatus) > rq) 351121440Sjhb rq = AGP_MODE_GET_ARQSZ(mstatus); 352121440Sjhb 353121440Sjhb /* Calibration cycle - don't allow override by mode register */ 354121440Sjhb cal = AGP_MODE_GET_CAL(tstatus); 355121440Sjhb if (AGP_MODE_GET_CAL(mstatus) < cal) 356121440Sjhb cal = AGP_MODE_GET_CAL(mstatus); 357121440Sjhb 358121440Sjhb /* SBA must be supported for AGP v3. */ 359121440Sjhb sba = 1; 360121440Sjhb 361121440Sjhb /* Set FW if all three support it. */ 362121440Sjhb fw = (AGP_MODE_GET_FW(tstatus) 363121440Sjhb & AGP_MODE_GET_FW(mstatus) 364121440Sjhb & AGP_MODE_GET_FW(mode)); 365121440Sjhb 366121440Sjhb /* Figure out the max rate */ 367121440Sjhb rate = (AGP_MODE_GET_RATE(tstatus) 368121440Sjhb & AGP_MODE_GET_RATE(mstatus) 369121440Sjhb & AGP_MODE_GET_RATE(mode)); 370121440Sjhb if (rate & AGP_MODE_V3_RATE_8x) 371121440Sjhb rate = AGP_MODE_V3_RATE_8x; 372121440Sjhb else 373121440Sjhb rate = AGP_MODE_V3_RATE_4x; 374121440Sjhb if (bootverbose) 375121440Sjhb device_printf(dev, "Setting AGP v3 mode %d\n", rate * 4); 376121440Sjhb 377121440Sjhb pci_write_config(dev, agp_find_caps(dev) + AGP_COMMAND, 0, 4); 378121440Sjhb 379121440Sjhb /* Construct the new mode word and tell the hardware */ 380161222Sjkim command = 0; 381121440Sjhb command = AGP_MODE_SET_RQ(0, rq); 382121440Sjhb command = AGP_MODE_SET_ARQSZ(command, arqsz); 383121440Sjhb command = AGP_MODE_SET_CAL(command, cal); 384121440Sjhb command = AGP_MODE_SET_SBA(command, sba); 385121440Sjhb command = AGP_MODE_SET_FW(command, fw); 386121440Sjhb command = AGP_MODE_SET_RATE(command, rate); 387161222Sjkim command = AGP_MODE_SET_MODE_3(command, 1); 388121440Sjhb command = AGP_MODE_SET_AGP(command, 1); 389121440Sjhb pci_write_config(dev, agp_find_caps(dev) + AGP_COMMAND, command, 4); 390121440Sjhb pci_write_config(mdev, agp_find_caps(mdev) + AGP_COMMAND, command, 4); 391121440Sjhb 392121440Sjhb return 0; 393121440Sjhb} 394121440Sjhb 395121440Sjhbstatic int 396121440Sjhbagp_v2_enable(device_t dev, device_t mdev, u_int32_t mode) 397121440Sjhb{ 398121440Sjhb u_int32_t tstatus, mstatus; 399121440Sjhb u_int32_t command; 400121440Sjhb int rq, sba, fw, rate; 401121440Sjhb 40261452Sdfr tstatus = pci_read_config(dev, agp_find_caps(dev) + AGP_STATUS, 4); 40361452Sdfr mstatus = pci_read_config(mdev, agp_find_caps(mdev) + AGP_STATUS, 4); 40461452Sdfr 40561452Sdfr /* Set RQ to the min of mode, tstatus and mstatus */ 40661452Sdfr rq = AGP_MODE_GET_RQ(mode); 40761452Sdfr if (AGP_MODE_GET_RQ(tstatus) < rq) 40861452Sdfr rq = AGP_MODE_GET_RQ(tstatus); 40961452Sdfr if (AGP_MODE_GET_RQ(mstatus) < rq) 41061452Sdfr rq = AGP_MODE_GET_RQ(mstatus); 41161452Sdfr 41261452Sdfr /* Set SBA if all three can deal with SBA */ 41361452Sdfr sba = (AGP_MODE_GET_SBA(tstatus) 41461452Sdfr & AGP_MODE_GET_SBA(mstatus) 41561452Sdfr & AGP_MODE_GET_SBA(mode)); 41661452Sdfr 41761452Sdfr /* Similar for FW */ 41861452Sdfr fw = (AGP_MODE_GET_FW(tstatus) 41961452Sdfr & AGP_MODE_GET_FW(mstatus) 42061452Sdfr & AGP_MODE_GET_FW(mode)); 42161452Sdfr 42261452Sdfr /* Figure out the max rate */ 42361452Sdfr rate = (AGP_MODE_GET_RATE(tstatus) 42461452Sdfr & AGP_MODE_GET_RATE(mstatus) 42561452Sdfr & AGP_MODE_GET_RATE(mode)); 426121440Sjhb if (rate & AGP_MODE_V2_RATE_4x) 427121440Sjhb rate = AGP_MODE_V2_RATE_4x; 428121440Sjhb else if (rate & AGP_MODE_V2_RATE_2x) 429121440Sjhb rate = AGP_MODE_V2_RATE_2x; 43061452Sdfr else 431121440Sjhb rate = AGP_MODE_V2_RATE_1x; 432121440Sjhb if (bootverbose) 433121440Sjhb device_printf(dev, "Setting AGP v2 mode %d\n", rate); 43461452Sdfr 43561452Sdfr /* Construct the new mode word and tell the hardware */ 436161222Sjkim command = 0; 43761452Sdfr command = AGP_MODE_SET_RQ(0, rq); 43861452Sdfr command = AGP_MODE_SET_SBA(command, sba); 43961452Sdfr command = AGP_MODE_SET_FW(command, fw); 44061452Sdfr command = AGP_MODE_SET_RATE(command, rate); 44161452Sdfr command = AGP_MODE_SET_AGP(command, 1); 44261452Sdfr pci_write_config(dev, agp_find_caps(dev) + AGP_COMMAND, command, 4); 44361452Sdfr pci_write_config(mdev, agp_find_caps(mdev) + AGP_COMMAND, command, 4); 44461452Sdfr 44561452Sdfr return 0; 44661452Sdfr} 44761452Sdfr 448121440Sjhbint 449121440Sjhbagp_generic_enable(device_t dev, u_int32_t mode) 450121440Sjhb{ 451121440Sjhb device_t mdev = agp_find_display(); 452121440Sjhb u_int32_t tstatus, mstatus; 453121440Sjhb 454121440Sjhb if (!mdev) { 455121440Sjhb AGP_DPF("can't find display\n"); 456121440Sjhb return ENXIO; 457121440Sjhb } 458121440Sjhb 459121440Sjhb tstatus = pci_read_config(dev, agp_find_caps(dev) + AGP_STATUS, 4); 460121440Sjhb mstatus = pci_read_config(mdev, agp_find_caps(mdev) + AGP_STATUS, 4); 461121440Sjhb 462121440Sjhb /* 463121440Sjhb * Check display and bridge for AGP v3 support. AGP v3 allows 464121440Sjhb * more variety in topology than v2, e.g. multiple AGP devices 465121440Sjhb * attached to one bridge, or multiple AGP bridges in one 466121440Sjhb * system. This doesn't attempt to address those situations, 467121440Sjhb * but should work fine for a classic single AGP slot system 468121440Sjhb * with AGP v3. 469121440Sjhb */ 470161222Sjkim if (AGP_MODE_GET_MODE_3(mode) && 471161222Sjkim AGP_MODE_GET_MODE_3(tstatus) && 472161222Sjkim AGP_MODE_GET_MODE_3(mstatus)) 473121440Sjhb return (agp_v3_enable(dev, mdev, mode)); 474121440Sjhb else 475121440Sjhb return (agp_v2_enable(dev, mdev, mode)); 476121440Sjhb} 477121440Sjhb 47861452Sdfrstruct agp_memory * 47961452Sdfragp_generic_alloc_memory(device_t dev, int type, vm_size_t size) 48061452Sdfr{ 48161452Sdfr struct agp_softc *sc = device_get_softc(dev); 48261452Sdfr struct agp_memory *mem; 48361452Sdfr 48461452Sdfr if ((size & (AGP_PAGE_SIZE - 1)) != 0) 48561452Sdfr return 0; 48661452Sdfr 48761452Sdfr if (sc->as_allocated + size > sc->as_maxmem) 48861452Sdfr return 0; 48961452Sdfr 49063010Sdfr if (type != 0) { 49163010Sdfr printf("agp_generic_alloc_memory: unsupported type %d\n", 49263010Sdfr type); 49363010Sdfr return 0; 49463010Sdfr } 49563010Sdfr 496111119Simp mem = malloc(sizeof *mem, M_AGP, M_WAITOK); 49761452Sdfr mem->am_id = sc->as_nextid++; 49861452Sdfr mem->am_size = size; 49963010Sdfr mem->am_type = 0; 50061452Sdfr mem->am_obj = vm_object_allocate(OBJT_DEFAULT, atop(round_page(size))); 50161452Sdfr mem->am_physical = 0; 50261452Sdfr mem->am_offset = 0; 50361452Sdfr mem->am_is_bound = 0; 50461452Sdfr TAILQ_INSERT_TAIL(&sc->as_memory, mem, am_link); 50561452Sdfr sc->as_allocated += size; 50661452Sdfr 50761452Sdfr return mem; 50861452Sdfr} 50961452Sdfr 51061452Sdfrint 51161452Sdfragp_generic_free_memory(device_t dev, struct agp_memory *mem) 51261452Sdfr{ 51361452Sdfr struct agp_softc *sc = device_get_softc(dev); 51461452Sdfr 51561452Sdfr if (mem->am_is_bound) 51661452Sdfr return EBUSY; 51761452Sdfr 51861452Sdfr sc->as_allocated -= mem->am_size; 51961452Sdfr TAILQ_REMOVE(&sc->as_memory, mem, am_link); 52061452Sdfr vm_object_deallocate(mem->am_obj); 52161452Sdfr free(mem, M_AGP); 52261452Sdfr return 0; 52361452Sdfr} 52461452Sdfr 52561452Sdfrint 52661452Sdfragp_generic_bind_memory(device_t dev, struct agp_memory *mem, 52761452Sdfr vm_offset_t offset) 52861452Sdfr{ 52961452Sdfr struct agp_softc *sc = device_get_softc(dev); 53061452Sdfr vm_offset_t i, j, k; 53161452Sdfr vm_page_t m; 53261452Sdfr int error; 53361452Sdfr 534129601Smux /* Do some sanity checks first. */ 535190169Srnoland if ((offset & (AGP_PAGE_SIZE - 1)) != 0 || 536129601Smux offset + mem->am_size > AGP_GET_APERTURE(dev)) { 53761452Sdfr device_printf(dev, "binding memory at bad offset %#x\n", 538129601Smux (int)offset); 53961452Sdfr return EINVAL; 54061452Sdfr } 54161452Sdfr 54261452Sdfr /* 543129601Smux * Allocate the pages early, before acquiring the lock, 544209793Skib * because vm_page_grab() may sleep and we can't hold a mutex 545209793Skib * while sleeping. 54661452Sdfr */ 547136852Salc VM_OBJECT_LOCK(mem->am_obj); 54861452Sdfr for (i = 0; i < mem->am_size; i += PAGE_SIZE) { 54961452Sdfr /* 55061452Sdfr * Find a page from the object and wire it 55161452Sdfr * down. This page will be mapped using one or more 55261452Sdfr * entries in the GATT (assuming that PAGE_SIZE >= 55361452Sdfr * AGP_PAGE_SIZE. If this is the first call to bind, 55461452Sdfr * the pages will be allocated and zeroed. 55561452Sdfr */ 55661452Sdfr m = vm_page_grab(mem->am_obj, OFF_TO_IDX(i), 557101647Salc VM_ALLOC_WIRED | VM_ALLOC_ZERO | VM_ALLOC_RETRY); 558188247Swkoszek AGP_DPF("found page pa=%#jx\n", (uintmax_t)VM_PAGE_TO_PHYS(m)); 559129601Smux } 560136852Salc VM_OBJECT_UNLOCK(mem->am_obj); 56161452Sdfr 562129601Smux mtx_lock(&sc->as_lock); 563129601Smux 564129601Smux if (mem->am_is_bound) { 565129601Smux device_printf(dev, "memory already bound\n"); 566129601Smux error = EINVAL; 567136852Salc VM_OBJECT_LOCK(mem->am_obj); 568186433Skib i = 0; 569129601Smux goto bad; 570129601Smux } 571129601Smux 572129601Smux /* 573129601Smux * Bind the individual pages and flush the chipset's 574129601Smux * TLB. 575129601Smux */ 576136852Salc VM_OBJECT_LOCK(mem->am_obj); 577129601Smux for (i = 0; i < mem->am_size; i += PAGE_SIZE) { 578129601Smux m = vm_page_lookup(mem->am_obj, OFF_TO_IDX(i)); 579129601Smux 58061452Sdfr /* 58161452Sdfr * Install entries in the GATT, making sure that if 58261452Sdfr * AGP_PAGE_SIZE < PAGE_SIZE and mem->am_size is not 58361452Sdfr * aligned to PAGE_SIZE, we don't modify too many GATT 58461452Sdfr * entries. 58561452Sdfr */ 58661452Sdfr for (j = 0; j < PAGE_SIZE && i + j < mem->am_size; 58761452Sdfr j += AGP_PAGE_SIZE) { 58861452Sdfr vm_offset_t pa = VM_PAGE_TO_PHYS(m) + j; 589188247Swkoszek AGP_DPF("binding offset %#jx to pa %#jx\n", 590188247Swkoszek (uintmax_t)offset + i + j, (uintmax_t)pa); 59161452Sdfr error = AGP_BIND_PAGE(dev, offset + i + j, pa); 59261452Sdfr if (error) { 59361452Sdfr /* 59461452Sdfr * Bail out. Reverse all the mappings 59561452Sdfr * and unwire the pages. 59661452Sdfr */ 59761452Sdfr for (k = 0; k < i + j; k += AGP_PAGE_SIZE) 59861452Sdfr AGP_UNBIND_PAGE(dev, offset + k); 599129601Smux goto bad; 60061452Sdfr } 60161452Sdfr } 60261452Sdfr vm_page_wakeup(m); 60361452Sdfr } 604136852Salc VM_OBJECT_UNLOCK(mem->am_obj); 60561452Sdfr 60661452Sdfr /* 60761452Sdfr * Flush the cpu cache since we are providing a new mapping 60861452Sdfr * for these pages. 60961452Sdfr */ 61061452Sdfr agp_flush_cache(); 61161452Sdfr 61261452Sdfr /* 61361452Sdfr * Make sure the chipset gets the new mappings. 61461452Sdfr */ 61561452Sdfr AGP_FLUSH_TLB(dev); 61661452Sdfr 61761452Sdfr mem->am_offset = offset; 61861452Sdfr mem->am_is_bound = 1; 61961452Sdfr 620129579Smux mtx_unlock(&sc->as_lock); 62161452Sdfr 62261452Sdfr return 0; 623129601Smuxbad: 624129601Smux mtx_unlock(&sc->as_lock); 625136852Salc VM_OBJECT_LOCK_ASSERT(mem->am_obj, MA_OWNED); 626186433Skib for (k = 0; k < mem->am_size; k += PAGE_SIZE) { 627186433Skib m = vm_page_lookup(mem->am_obj, OFF_TO_IDX(k)); 628186433Skib if (k >= i) 629186433Skib vm_page_wakeup(m); 630207574Salc vm_page_lock(m); 631129601Smux vm_page_unwire(m, 0); 632207574Salc vm_page_unlock(m); 633129601Smux } 634129601Smux VM_OBJECT_UNLOCK(mem->am_obj); 635129601Smux 636129601Smux return error; 63761452Sdfr} 63861452Sdfr 63961452Sdfrint 64061452Sdfragp_generic_unbind_memory(device_t dev, struct agp_memory *mem) 64161452Sdfr{ 64261452Sdfr struct agp_softc *sc = device_get_softc(dev); 64361452Sdfr vm_page_t m; 64461452Sdfr int i; 64561452Sdfr 646129579Smux mtx_lock(&sc->as_lock); 64761452Sdfr 64861452Sdfr if (!mem->am_is_bound) { 64961452Sdfr device_printf(dev, "memory is not bound\n"); 650129579Smux mtx_unlock(&sc->as_lock); 65161452Sdfr return EINVAL; 65261452Sdfr } 65361452Sdfr 65461452Sdfr 65561452Sdfr /* 65661452Sdfr * Unbind the individual pages and flush the chipset's 65761452Sdfr * TLB. Unwire the pages so they can be swapped. 65861452Sdfr */ 65961452Sdfr for (i = 0; i < mem->am_size; i += AGP_PAGE_SIZE) 66061452Sdfr AGP_UNBIND_PAGE(dev, mem->am_offset + i); 661116555Salc VM_OBJECT_LOCK(mem->am_obj); 66261452Sdfr for (i = 0; i < mem->am_size; i += PAGE_SIZE) { 66361452Sdfr m = vm_page_lookup(mem->am_obj, atop(i)); 664207574Salc vm_page_lock(m); 66561452Sdfr vm_page_unwire(m, 0); 666207574Salc vm_page_unlock(m); 66761452Sdfr } 668116555Salc VM_OBJECT_UNLOCK(mem->am_obj); 66961452Sdfr 67061452Sdfr agp_flush_cache(); 67161452Sdfr AGP_FLUSH_TLB(dev); 67261452Sdfr 67361452Sdfr mem->am_offset = 0; 67461452Sdfr mem->am_is_bound = 0; 67561452Sdfr 676129579Smux mtx_unlock(&sc->as_lock); 67761452Sdfr 67861452Sdfr return 0; 67961452Sdfr} 68061452Sdfr 68161452Sdfr/* Helper functions for implementing user/kernel api */ 68261452Sdfr 68361452Sdfrstatic int 68461452Sdfragp_acquire_helper(device_t dev, enum agp_acquire_state state) 68561452Sdfr{ 68661452Sdfr struct agp_softc *sc = device_get_softc(dev); 68761452Sdfr 68861452Sdfr if (sc->as_state != AGP_ACQUIRE_FREE) 68961452Sdfr return EBUSY; 69061452Sdfr sc->as_state = state; 69161452Sdfr 69261452Sdfr return 0; 69361452Sdfr} 69461452Sdfr 69561452Sdfrstatic int 69661452Sdfragp_release_helper(device_t dev, enum agp_acquire_state state) 69761452Sdfr{ 69861452Sdfr struct agp_softc *sc = device_get_softc(dev); 69961452Sdfr 70061452Sdfr if (sc->as_state == AGP_ACQUIRE_FREE) 70161452Sdfr return 0; 70261452Sdfr 70361452Sdfr if (sc->as_state != state) 70461452Sdfr return EBUSY; 70561452Sdfr 70661452Sdfr sc->as_state = AGP_ACQUIRE_FREE; 70761452Sdfr return 0; 70861452Sdfr} 70961452Sdfr 71061452Sdfrstatic struct agp_memory * 71161452Sdfragp_find_memory(device_t dev, int id) 71261452Sdfr{ 71361452Sdfr struct agp_softc *sc = device_get_softc(dev); 71461452Sdfr struct agp_memory *mem; 71561452Sdfr 71661452Sdfr AGP_DPF("searching for memory block %d\n", id); 71761452Sdfr TAILQ_FOREACH(mem, &sc->as_memory, am_link) { 71861452Sdfr AGP_DPF("considering memory block %d\n", mem->am_id); 71961452Sdfr if (mem->am_id == id) 72061452Sdfr return mem; 72161452Sdfr } 72261452Sdfr return 0; 72361452Sdfr} 72461452Sdfr 72561452Sdfr/* Implementation of the userland ioctl api */ 72661452Sdfr 72761452Sdfrstatic int 72861452Sdfragp_info_user(device_t dev, agp_info *info) 72961452Sdfr{ 73061452Sdfr struct agp_softc *sc = device_get_softc(dev); 73161452Sdfr 73261452Sdfr bzero(info, sizeof *info); 73361452Sdfr info->bridge_id = pci_get_devid(dev); 73461452Sdfr info->agp_mode = 73561452Sdfr pci_read_config(dev, agp_find_caps(dev) + AGP_STATUS, 4); 736214603Snwhitehorn if (sc->as_aperture) 737214603Snwhitehorn info->aper_base = rman_get_start(sc->as_aperture); 738214603Snwhitehorn else 739214603Snwhitehorn info->aper_base = 0; 74061452Sdfr info->aper_size = AGP_GET_APERTURE(dev) >> 20; 74161452Sdfr info->pg_total = info->pg_system = sc->as_maxmem >> AGP_PAGE_SHIFT; 74261452Sdfr info->pg_used = sc->as_allocated >> AGP_PAGE_SHIFT; 74361452Sdfr 74461452Sdfr return 0; 74561452Sdfr} 74661452Sdfr 74761452Sdfrstatic int 74861452Sdfragp_setup_user(device_t dev, agp_setup *setup) 74961452Sdfr{ 75061452Sdfr return AGP_ENABLE(dev, setup->agp_mode); 75161452Sdfr} 75261452Sdfr 75361452Sdfrstatic int 75461452Sdfragp_allocate_user(device_t dev, agp_allocate *alloc) 75561452Sdfr{ 75661452Sdfr struct agp_memory *mem; 75761452Sdfr 75861452Sdfr mem = AGP_ALLOC_MEMORY(dev, 75961452Sdfr alloc->type, 76061452Sdfr alloc->pg_count << AGP_PAGE_SHIFT); 76163010Sdfr if (mem) { 76263010Sdfr alloc->key = mem->am_id; 76363010Sdfr alloc->physical = mem->am_physical; 76463010Sdfr return 0; 76563010Sdfr } else { 76663010Sdfr return ENOMEM; 76763010Sdfr } 76861452Sdfr} 76961452Sdfr 77061452Sdfrstatic int 77161452Sdfragp_deallocate_user(device_t dev, int id) 77261452Sdfr{ 773201758Smbr struct agp_memory *mem = agp_find_memory(dev, id); 77461452Sdfr 77561452Sdfr if (mem) { 77661452Sdfr AGP_FREE_MEMORY(dev, mem); 77761452Sdfr return 0; 77861452Sdfr } else { 77961452Sdfr return ENOENT; 78061452Sdfr } 78161452Sdfr} 78261452Sdfr 78361452Sdfrstatic int 78461452Sdfragp_bind_user(device_t dev, agp_bind *bind) 78561452Sdfr{ 78661452Sdfr struct agp_memory *mem = agp_find_memory(dev, bind->key); 78761452Sdfr 78861452Sdfr if (!mem) 78961452Sdfr return ENOENT; 79061452Sdfr 79161452Sdfr return AGP_BIND_MEMORY(dev, mem, bind->pg_start << AGP_PAGE_SHIFT); 79261452Sdfr} 79361452Sdfr 79461452Sdfrstatic int 79561452Sdfragp_unbind_user(device_t dev, agp_unbind *unbind) 79661452Sdfr{ 79761452Sdfr struct agp_memory *mem = agp_find_memory(dev, unbind->key); 79861452Sdfr 79961452Sdfr if (!mem) 80061452Sdfr return ENOENT; 80161452Sdfr 80261452Sdfr return AGP_UNBIND_MEMORY(dev, mem); 80361452Sdfr} 80461452Sdfr 80561452Sdfrstatic int 806235782Skibagp_chipset_flush(device_t dev) 807235782Skib{ 808235782Skib 809235782Skib return (AGP_CHIPSET_FLUSH(dev)); 810235782Skib} 811235782Skib 812235782Skibstatic int 813130585Sphkagp_open(struct cdev *kdev, int oflags, int devtype, struct thread *td) 81461452Sdfr{ 815191057Sed device_t dev = kdev->si_drv1; 81661452Sdfr struct agp_softc *sc = device_get_softc(dev); 81761452Sdfr 81861452Sdfr if (!sc->as_isopen) { 81961452Sdfr sc->as_isopen = 1; 82061452Sdfr device_busy(dev); 82161452Sdfr } 82261452Sdfr 82361452Sdfr return 0; 82461452Sdfr} 82561452Sdfr 82661452Sdfrstatic int 827130585Sphkagp_close(struct cdev *kdev, int fflag, int devtype, struct thread *td) 82861452Sdfr{ 829191057Sed device_t dev = kdev->si_drv1; 83061452Sdfr struct agp_softc *sc = device_get_softc(dev); 83186976Sru struct agp_memory *mem; 83261452Sdfr 83361452Sdfr /* 83461452Sdfr * Clear the GATT and force release on last close 83561452Sdfr */ 83686976Sru while ((mem = TAILQ_FIRST(&sc->as_memory)) != 0) { 83786976Sru if (mem->am_is_bound) 83886976Sru AGP_UNBIND_MEMORY(dev, mem); 83986976Sru AGP_FREE_MEMORY(dev, mem); 84086976Sru } 84161452Sdfr if (sc->as_state == AGP_ACQUIRE_USER) 84261452Sdfr agp_release_helper(dev, AGP_ACQUIRE_USER); 84361452Sdfr sc->as_isopen = 0; 84461452Sdfr device_unbusy(dev); 84561452Sdfr 84661452Sdfr return 0; 84761452Sdfr} 84861452Sdfr 84961452Sdfrstatic int 850130585Sphkagp_ioctl(struct cdev *kdev, u_long cmd, caddr_t data, int fflag, struct thread *td) 85161452Sdfr{ 852191057Sed device_t dev = kdev->si_drv1; 85361452Sdfr 85461452Sdfr switch (cmd) { 85561452Sdfr case AGPIOC_INFO: 85661452Sdfr return agp_info_user(dev, (agp_info *) data); 85761452Sdfr 85861452Sdfr case AGPIOC_ACQUIRE: 85961452Sdfr return agp_acquire_helper(dev, AGP_ACQUIRE_USER); 86061452Sdfr 86161452Sdfr case AGPIOC_RELEASE: 86261452Sdfr return agp_release_helper(dev, AGP_ACQUIRE_USER); 86361452Sdfr 86461452Sdfr case AGPIOC_SETUP: 86561452Sdfr return agp_setup_user(dev, (agp_setup *)data); 86661452Sdfr 86761452Sdfr case AGPIOC_ALLOCATE: 86861452Sdfr return agp_allocate_user(dev, (agp_allocate *)data); 86961452Sdfr 87061452Sdfr case AGPIOC_DEALLOCATE: 87161452Sdfr return agp_deallocate_user(dev, *(int *) data); 87261452Sdfr 87361452Sdfr case AGPIOC_BIND: 87461452Sdfr return agp_bind_user(dev, (agp_bind *)data); 87561452Sdfr 87661452Sdfr case AGPIOC_UNBIND: 87761452Sdfr return agp_unbind_user(dev, (agp_unbind *)data); 87861452Sdfr 879235782Skib case AGPIOC_CHIPSET_FLUSH: 880235782Skib return agp_chipset_flush(dev); 88161452Sdfr } 88261452Sdfr 88361452Sdfr return EINVAL; 88461452Sdfr} 88561452Sdfr 88661452Sdfrstatic int 887201223Srnolandagp_mmap(struct cdev *kdev, vm_ooffset_t offset, vm_paddr_t *paddr, 888201223Srnoland int prot, vm_memattr_t *memattr) 88961452Sdfr{ 890191057Sed device_t dev = kdev->si_drv1; 89161452Sdfr struct agp_softc *sc = device_get_softc(dev); 89261452Sdfr 89361452Sdfr if (offset > AGP_GET_APERTURE(dev)) 89461452Sdfr return -1; 895214603Snwhitehorn if (sc->as_aperture == NULL) 896214603Snwhitehorn return -1; 897111462Smux *paddr = rman_get_start(sc->as_aperture) + offset; 898111462Smux return 0; 89961452Sdfr} 90061452Sdfr 90161452Sdfr/* Implementation of the kernel api */ 90261452Sdfr 90361452Sdfrdevice_t 90461452Sdfragp_find_device() 90561452Sdfr{ 906154486Sjhb device_t *children, child; 907153568Sjhb int i, count; 908153568Sjhb 90961452Sdfr if (!agp_devclass) 910153568Sjhb return NULL; 911153568Sjhb if (devclass_get_devices(agp_devclass, &children, &count) != 0) 912153568Sjhb return NULL; 913154486Sjhb child = NULL; 914153568Sjhb for (i = 0; i < count; i++) { 915154486Sjhb if (device_is_attached(children[i])) { 916154486Sjhb child = children[i]; 917154486Sjhb break; 918154486Sjhb } 919153568Sjhb } 920154486Sjhb free(children, M_TEMP); 921154486Sjhb return child; 92261452Sdfr} 92361452Sdfr 92461452Sdfrenum agp_acquire_state 92561452Sdfragp_state(device_t dev) 92661452Sdfr{ 92761452Sdfr struct agp_softc *sc = device_get_softc(dev); 92861452Sdfr return sc->as_state; 92961452Sdfr} 93061452Sdfr 93161452Sdfrvoid 93261452Sdfragp_get_info(device_t dev, struct agp_info *info) 93361452Sdfr{ 93461452Sdfr struct agp_softc *sc = device_get_softc(dev); 93561452Sdfr 93661452Sdfr info->ai_mode = 93761452Sdfr pci_read_config(dev, agp_find_caps(dev) + AGP_STATUS, 4); 938214603Snwhitehorn if (sc->as_aperture != NULL) 939214603Snwhitehorn info->ai_aperture_base = rman_get_start(sc->as_aperture); 940214603Snwhitehorn else 941214603Snwhitehorn info->ai_aperture_base = 0; 942214603Snwhitehorn info->ai_aperture_size = AGP_GET_APERTURE(dev); 94361452Sdfr info->ai_memory_allowed = sc->as_maxmem; 94461452Sdfr info->ai_memory_used = sc->as_allocated; 94561452Sdfr} 94661452Sdfr 94761452Sdfrint 94861452Sdfragp_acquire(device_t dev) 94961452Sdfr{ 95061452Sdfr return agp_acquire_helper(dev, AGP_ACQUIRE_KERNEL); 95161452Sdfr} 95261452Sdfr 95361452Sdfrint 95461452Sdfragp_release(device_t dev) 95561452Sdfr{ 95661452Sdfr return agp_release_helper(dev, AGP_ACQUIRE_KERNEL); 95761452Sdfr} 95861452Sdfr 95961452Sdfrint 96061452Sdfragp_enable(device_t dev, u_int32_t mode) 96161452Sdfr{ 96261452Sdfr return AGP_ENABLE(dev, mode); 96361452Sdfr} 96461452Sdfr 96561452Sdfrvoid *agp_alloc_memory(device_t dev, int type, vm_size_t bytes) 96661452Sdfr{ 96761452Sdfr return (void *) AGP_ALLOC_MEMORY(dev, type, bytes); 96861452Sdfr} 96961452Sdfr 97061452Sdfrvoid agp_free_memory(device_t dev, void *handle) 97161452Sdfr{ 97261452Sdfr struct agp_memory *mem = (struct agp_memory *) handle; 97361452Sdfr AGP_FREE_MEMORY(dev, mem); 97461452Sdfr} 97561452Sdfr 97661452Sdfrint agp_bind_memory(device_t dev, void *handle, vm_offset_t offset) 97761452Sdfr{ 97861452Sdfr struct agp_memory *mem = (struct agp_memory *) handle; 97961452Sdfr return AGP_BIND_MEMORY(dev, mem, offset); 98061452Sdfr} 98161452Sdfr 98261452Sdfrint agp_unbind_memory(device_t dev, void *handle) 98361452Sdfr{ 98461452Sdfr struct agp_memory *mem = (struct agp_memory *) handle; 98561452Sdfr return AGP_UNBIND_MEMORY(dev, mem); 98661452Sdfr} 98761452Sdfr 98861452Sdfrvoid agp_memory_info(device_t dev, void *handle, struct 98961452Sdfr agp_memory_info *mi) 99061452Sdfr{ 99161452Sdfr struct agp_memory *mem = (struct agp_memory *) handle; 99261452Sdfr 99361452Sdfr mi->ami_size = mem->am_size; 99461452Sdfr mi->ami_physical = mem->am_physical; 99561452Sdfr mi->ami_offset = mem->am_offset; 99661452Sdfr mi->ami_is_bound = mem->am_is_bound; 99761452Sdfr} 998