/* * Copyright 2008-2010, Axel Dörfler, axeld@pinc-software.de. * Copyright 2004-2006, Rudolf Cornelissen. All rights reserved. * * Distributed under the terms of the MIT License. */ // TODO: rethink the AGP interface for more than one bridge/device! // (should be done with the new driver API then) /* Notes: - currently we just setup all found devices with AGP interface to the same highest common mode, we don't distinquish different AGP busses. TODO: it might be a better idea to just setup one instead. - AGP3 defines 'asynchronous request size' and 'calibration cycle' fields in the status and command registers. Currently programming zero's which will make it work, although further optimisation is possible. - AGP3.5 also defines isochronous transfers which are not implemented here: the hardware keeps them disabled by default. */ #include #include #include #include #include #include #include #include #include #define TRACE_AGP #ifdef TRACE_AGP # define TRACE(x...) dprintf("\33[36mAGP:\33[0m " x) #else # define TRACE(x...) ; #endif #define ERROR(x...) dprintf("\33[36mAGP:\33[0m " x) #define MAX_DEVICES 8 #define AGP_ID(address) (address) #define AGP_STATUS(address) (address + 4) #define AGP_COMMAND(address) (address + 8) /* read and write to PCI config space */ #define get_pci_config(info, offset, size) \ (sPCI->read_pci_config((info).bus, (info).device, (info).function, \ (offset), (size))) #define set_pci_config(info, offset, size, value) \ (sPCI->write_pci_config((info).bus, (info).device, (info).function, \ (offset), (size), (value))) #define RESERVED_APERTURE 0x80000000 #define ALLOCATED_APERTURE 0x40000000 #define BIND_APERTURE 0x20000000 #define APERTURE_PUBLIC_FLAGS_MASK 0x0000ffff struct aperture_memory { aperture_memory *next; aperture_memory *hash_link; addr_t base; size_t size; uint32 flags; #if !defined(GART_TEST) union { vm_page **pages; vm_page *page; }; #ifdef DEBUG_PAGE_ACCESS thread_id allocating_thread; #endif #else area_id area; #endif }; class Aperture; class MemoryHashDefinition { public: typedef addr_t KeyType; typedef aperture_memory ValueType; MemoryHashDefinition(aperture_info &info) : fInfo(info) {} size_t HashKey(const KeyType &base) const { return (base - fInfo.base) / B_PAGE_SIZE; } size_t Hash(aperture_memory *memory) const { return (memory->base - fInfo.base) / B_PAGE_SIZE; } bool Compare(const KeyType &base, aperture_memory *memory) const { return base == memory->base; } aperture_memory *&GetLink(aperture_memory *memory) const { return memory->hash_link; } private: aperture_info &fInfo; }; typedef BOpenHashTable MemoryHashTable; struct agp_device_info { uint8 address; /* location of AGP interface in PCI capabilities */ agp_info info; }; class Aperture { public: Aperture(agp_gart_bus_module_info *module, void *aperture); ~Aperture(); status_t InitCheck() const { return fLock.sem >= B_OK ? B_OK : fLock.sem; } void DeleteMemory(aperture_memory *memory); aperture_memory *CreateMemory(size_t size, size_t alignment, uint32 flags); status_t AllocateMemory(aperture_memory *memory, uint32 flags); status_t UnbindMemory(aperture_memory *memory); status_t BindMemory(aperture_memory *memory, addr_t base, size_t size); status_t GetInfo(aperture_info *info); aperture_memory *GetMemory(addr_t base) { return fHashTable.Lookup(base); } addr_t Base() const { return fInfo.base; } addr_t Size() const { return fInfo.size; } int32 ID() const { return fID; } struct lock &Lock() { return fLock; } private: bool _AdaptToReserved(addr_t &base, size_t &size, int32 *_offset = NULL); void _Free(aperture_memory *memory); void _Remove(aperture_memory *memory); status_t _Insert(aperture_memory *memory, size_t size, size_t alignment, uint32 flags); struct lock fLock; agp_gart_bus_module_info *fModule; int32 fID; aperture_info fInfo; MemoryHashTable fHashTable; aperture_memory *fFirstMemory; void *fPrivateAperture; public: Aperture *fNext; }; class ApertureHashDefinition { public: typedef int32 KeyType; typedef Aperture ValueType; size_t HashKey(const KeyType &id) const { return id; } size_t Hash(Aperture *aperture) const { return aperture->ID(); } bool Compare(const KeyType &id, Aperture *aperture) const { return id == aperture->ID(); } Aperture *&GetLink(Aperture *aperture) const { return aperture->fNext; } }; typedef BOpenHashTable ApertureHashTable; static agp_device_info sDeviceInfos[MAX_DEVICES]; static uint32 sDeviceCount; static pci_module_info *sPCI; static int32 sAcquired; static ApertureHashTable sApertureHashTable; static int32 sNextApertureID; static struct lock sLock; // #pragma mark - private support functions /*! Makes sure that all bits lower than the maximum supported rate is set. */ static uint32 fix_rate_support(uint32 command) { if ((command & AGP_3_MODE) != 0) { if ((command & AGP_3_8x) != 0) command |= AGP_3_4x; command &= ~AGP_RATE_MASK | AGP_3_8x | AGP_3_4x; command |= AGP_SBA; // SBA is required for AGP3 } else { /* AGP 2.0 scheme applies */ if ((command & AGP_2_4x) != 0) command |= AGP_2_2x; if ((command & AGP_2_2x) != 0) command |= AGP_2_1x; } return command; } /*! Makes sure that only the highest rate bit is set. */ static uint32 fix_rate_command(uint32 command) { if ((command & AGP_3_MODE) != 0) { if ((command & AGP_3_8x) != 0) command &= ~AGP_3_4x; } else { /* AGP 2.0 scheme applies */ if ((command & AGP_2_4x) != 0) command &= ~(AGP_2_2x | AGP_2_1x); if ((command & AGP_2_2x) != 0) command &= ~AGP_2_1x; } return command; } /*! Checks the capabilities of the device, and removes everything from \a command that the device does not support. */ static void check_capabilities(agp_device_info &deviceInfo, uint32 &command) { uint32 agpStatus = deviceInfo.info.interface.status; if (deviceInfo.info.class_base == PCI_bridge) { // make sure the AGP rate support mask is correct // (ie. has the lower bits set) agpStatus = fix_rate_support(agpStatus); } TRACE("device %u.%u.%u has AGP capabilities %" B_PRIx32 "\n", deviceInfo.info.bus, deviceInfo.info.device, deviceInfo.info.function, agpStatus); // block non-supported AGP modes command &= (agpStatus & (AGP_3_MODE | AGP_RATE_MASK)) | ~(AGP_3_MODE | AGP_RATE_MASK); // If no AGP mode is supported at all, nothing remains: // devices exist that have the AGP style connector with AGP style registers, // but not the features! // (confirmed Matrox Millenium II AGP for instance) if ((agpStatus & AGP_RATE_MASK) == 0) command = 0; // block side band adressing if not supported if ((agpStatus & AGP_SBA) == 0) command &= ~AGP_SBA; // block fast writes if not supported if ((agpStatus & AGP_FAST_WRITE) == 0) command &= ~AGP_FAST_WRITE; // adjust maximum request depth to least depth supported // note: this is writable only in the graphics card uint8 requestDepth = ((agpStatus & AGP_REQUEST) >> AGP_REQUEST_SHIFT); if (requestDepth < ((command & AGP_REQUEST) >> AGP_REQUEST_SHIFT)) { command &= ~AGP_REQUEST; command |= (requestDepth << AGP_REQUEST_SHIFT); } } /*! Checks the PCI capabilities if the device is an AGP device */ static bool is_agp_device(pci_info &info, uint8 *_address) { // Check if device implements a list of capabilities if ((get_pci_config(info, PCI_status, 2) & PCI_status_capabilities) == 0) return false; // Get pointer to PCI capabilities list // (AGP devices only, no need to take cardbus into account) uint8 address = get_pci_config(info, PCI_capabilities_ptr, 1); while (true) { uint8 id = get_pci_config(info, address, 1); uint8 next = get_pci_config(info, address + 1, 1) & ~0x3; if (id == PCI_cap_id_agp) { // is an AGP device if (_address != NULL) *_address = address; return true; } if (next == 0) { // end of list break; } address = next; } return false; } static status_t get_next_agp_device(uint32 *_cookie, pci_info &info, agp_device_info &device) { uint32 index = *_cookie; // find devices for (; sPCI->get_nth_pci_info(index, &info) == B_OK; index++) { // is it a bridge or a graphics card? if ((info.class_base != PCI_bridge || info.class_sub != PCI_host) && info.class_base != PCI_display) continue; if (is_agp_device(info, &device.address)) { device.info.vendor_id = info.vendor_id; device.info.device_id = info.device_id; device.info.bus = info.bus; device.info.device = info.device; device.info.function = info.function; device.info.class_sub = info.class_sub; device.info.class_base = info.class_base; /* get the contents of the AGP registers from this device */ device.info.interface.capability_id = get_pci_config(info, AGP_ID(device.address), 4); device.info.interface.status = get_pci_config(info, AGP_STATUS(device.address), 4); device.info.interface.command = get_pci_config(info, AGP_COMMAND(device.address), 4); *_cookie = index + 1; return B_OK; } } return B_ENTRY_NOT_FOUND; } static void set_agp_command(agp_device_info &deviceInfo, uint32 command) { set_pci_config(deviceInfo.info, AGP_COMMAND(deviceInfo.address), 4, command); deviceInfo.info.interface.command = get_pci_config(deviceInfo.info, AGP_COMMAND(deviceInfo.address), 4); } static void set_pci_mode() { TRACE("set PCI mode on all AGP capable devices.\n"); // First program all graphics cards for (uint32 index = 0; index < sDeviceCount; index++) { agp_device_info &deviceInfo = sDeviceInfos[index]; if (deviceInfo.info.class_base != PCI_display) continue; set_agp_command(deviceInfo, 0); } // Then program all bridges - it's the other around for AGP mode for (uint32 index = 0; index < sDeviceCount; index++) { agp_device_info &deviceInfo = sDeviceInfos[index]; if (deviceInfo.info.class_base != PCI_bridge) continue; set_agp_command(deviceInfo, 0); } // Wait 10mS for the bridges to recover (failsafe!) // Note: some SiS bridge chipsets apparantly require 5mS to recover // or the master (graphics card) cannot be initialized correctly! snooze(10000); } status_t get_area_base_and_size(area_id area, addr_t &base, size_t &size) { area_info info; status_t status = get_area_info(area, &info); if (status < B_OK) return status; base = (addr_t)info.address; size = info.size; return B_OK; } Aperture * get_aperture(aperture_id id) { Autolock _(sLock); return sApertureHashTable.Lookup(id); } // #pragma mark - Aperture Aperture::Aperture(agp_gart_bus_module_info *module, void *aperture) : fModule(module), fInfo(), fHashTable(fInfo), fFirstMemory(NULL), fPrivateAperture(aperture) { fModule->get_aperture_info(fPrivateAperture, &fInfo); fID = atomic_add(&sNextApertureID, 1); init_lock(&fLock, "aperture"); } Aperture::~Aperture() { while (fFirstMemory != NULL) { DeleteMemory(fFirstMemory); } fModule->delete_aperture(fPrivateAperture); put_module(fModule->info.name); } status_t Aperture::GetInfo(aperture_info *info) { if (info == NULL) return B_BAD_VALUE; *info = fInfo; return B_OK; } void Aperture::DeleteMemory(aperture_memory *memory) { TRACE("delete memory %p\n", memory); UnbindMemory(memory); _Free(memory); _Remove(memory); fHashTable.Remove(memory); delete memory; } aperture_memory * Aperture::CreateMemory(size_t size, size_t alignment, uint32 flags) { aperture_memory *memory = new(std::nothrow) aperture_memory; if (memory == NULL) return NULL; status_t status = _Insert(memory, size, alignment, flags); if (status < B_OK) { ERROR("Aperture::CreateMemory(): did not find a free space large for " "this memory object\n"); delete memory; return NULL; } TRACE("create memory %p, base %" B_PRIxADDR ", size %" B_PRIxSIZE ", flags %" B_PRIx32 "\n", memory, memory->base, memory->size, flags); memory->flags = flags; #if !defined(GART_TEST) memory->pages = NULL; #else memory->area = -1; #endif fHashTable.Insert(memory); return memory; } bool Aperture::_AdaptToReserved(addr_t &base, size_t &size, int32 *_offset) { addr_t reservedEnd = fInfo.base + fInfo.reserved_size; if (reservedEnd <= base) return false; if (reservedEnd >= base + size) { size = 0; return true; } if (_offset != NULL) *_offset = reservedEnd - base; size -= reservedEnd - base; base = reservedEnd; return true; } status_t Aperture::AllocateMemory(aperture_memory *memory, uint32 flags) { // We don't need to allocate reserved memory - it's // already there for us to use addr_t base = memory->base; size_t size = memory->size; if (_AdaptToReserved(base, size)) { if (size == 0) { TRACE("allocation is made of reserved memory\n"); return B_OK; } memset((void *)memory->base, 0, memory->size - size); } TRACE("allocate %ld bytes out of %ld\n", size, memory->size); #if !defined(GART_TEST) uint32 count = size / B_PAGE_SIZE; if ((flags & B_APERTURE_NEED_PHYSICAL) != 0) { physical_address_restrictions restrictions = {}; #if B_HAIKU_PHYSICAL_BITS > 32 restrictions.high_address = (phys_addr_t)1 << 32; // TODO: Work-around until intel_gart can deal with physical // addresses > 4 GB. #endif memory->page = vm_page_allocate_page_run( PAGE_STATE_WIRED | VM_PAGE_ALLOC_CLEAR, count, &restrictions, VM_PRIORITY_SYSTEM); if (memory->page == NULL) { ERROR("Aperture::AllocateMemory(): vm_page_allocate_page_run() " "failed (with B_APERTURE_NEED_PHYSICAL)\n"); return B_NO_MEMORY; } } else { // Allocate table to hold the pages memory->pages = (vm_page **)malloc(count * sizeof(vm_page *)); if (memory->pages == NULL) return B_NO_MEMORY; #if B_HAIKU_PHYSICAL_BITS > 32 // TODO: Work-around until intel_gart can deal with physical // addresses > 4 GB. physical_address_restrictions restrictions = {}; restrictions.high_address = (phys_addr_t)1 << 32; vm_page* page = vm_page_allocate_page_run( PAGE_STATE_WIRED | VM_PAGE_ALLOC_CLEAR, count, &restrictions, VM_PRIORITY_SYSTEM); if (page == NULL) { ERROR("Aperture::AllocateMemory(): vm_page_allocate_page_run() " "failed (without B_APERTURE_NEED_PHYSICAL)\n"); return B_NO_MEMORY; } for (uint32 i = 0; i < count; i++) memory->pages[i] = page + i; #else vm_page_reservation reservation; vm_page_reserve_pages(&reservation, count, VM_PRIORITY_SYSTEM); for (uint32 i = 0; i < count; i++) { memory->pages[i] = vm_page_allocate_page(&reservation, PAGE_STATE_WIRED | VM_PAGE_ALLOC_CLEAR); } vm_page_unreserve_pages(&reservation); #endif } #ifdef DEBUG_PAGE_ACCESS memory->allocating_thread = find_thread(NULL); #endif #else // GART_TEST void *address; memory->area = create_area("GART memory", &address, B_ANY_KERNEL_ADDRESS, size, B_FULL_LOCK | ((flags & B_APERTURE_NEED_PHYSICAL) != 0 ? B_CONTIGUOUS : 0), B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA); if (memory->area < B_OK) { ERROR("Aperture::AllocateMemory(): create_area() failed\n"); return B_NO_MEMORY; } #endif memory->flags |= ALLOCATED_APERTURE; return B_OK; } status_t Aperture::UnbindMemory(aperture_memory *memory) { if ((memory->flags & BIND_APERTURE) == 0) return B_BAD_VALUE; // We must not unbind reserved memory addr_t base = memory->base; size_t size = memory->size; if (_AdaptToReserved(base, size) && size == 0) { memory->flags &= ~BIND_APERTURE; return B_OK; } addr_t start = base - Base(); TRACE("unbind %ld bytes at %lx\n", size, start); for (addr_t offset = 0; offset < memory->size; offset += B_PAGE_SIZE) { status_t status = fModule->unbind_page(fPrivateAperture, start + offset); if (status < B_OK) return status; } memory->flags &= ~BIND_APERTURE; fModule->flush_tlbs(fPrivateAperture); return B_OK; } status_t Aperture::BindMemory(aperture_memory *memory, addr_t address, size_t size) { bool physical = false; if ((memory->flags & ALLOCATED_APERTURE) != 0) { // We allocated this memory, get the base and size from there size = memory->size; physical = true; } // We don't need to bind reserved memory addr_t base = memory->base; int32 offset; if (_AdaptToReserved(base, size, &offset)) { if (size == 0) { TRACE("reserved memory already bound\n"); memory->flags |= BIND_APERTURE; return B_OK; } address += offset; } addr_t start = base - Base(); TRACE("bind %ld bytes at %lx\n", size, base); for (addr_t offset = 0; offset < size; offset += B_PAGE_SIZE) { phys_addr_t physicalAddress = 0; status_t status; if (!physical) { physical_entry entry; status = get_memory_map((void *)(address + offset), B_PAGE_SIZE, &entry, 1); if (status < B_OK) { ERROR("Aperture::BindMemory(): get_memory_map() failed\n"); return status; } physicalAddress = entry.address; } else { uint32 index = offset >> PAGE_SHIFT; vm_page *page; if ((memory->flags & B_APERTURE_NEED_PHYSICAL) != 0) page = memory->page + index; else page = memory->pages[index]; physicalAddress = (phys_addr_t)page->physical_page_number << PAGE_SHIFT; } status = fModule->bind_page(fPrivateAperture, start + offset, physicalAddress); if (status < B_OK) { ERROR("Aperture::BindMemory(): bind_page() failed\n"); return status; } } memory->flags |= BIND_APERTURE; fModule->flush_tlbs(fPrivateAperture); return B_OK; } void Aperture::_Free(aperture_memory *memory) { if ((memory->flags & ALLOCATED_APERTURE) == 0) return; #if !defined(GART_TEST) // Remove the stolen area from the allocation size_t size = memory->size; addr_t reservedEnd = fInfo.base + fInfo.reserved_size; if (memory->base < reservedEnd) size -= reservedEnd - memory->base; // Free previously allocated pages and page table uint32 count = size / B_PAGE_SIZE; if ((memory->flags & B_APERTURE_NEED_PHYSICAL) != 0) { vm_page *page = memory->page; for (uint32 i = 0; i < count; i++, page++) { DEBUG_PAGE_ACCESS_TRANSFER(page, memory->allocating_thread); vm_page_set_state(page, PAGE_STATE_FREE); } memory->page = NULL; } else { for (uint32 i = 0; i < count; i++) { DEBUG_PAGE_ACCESS_TRANSFER(memory->pages[i], memory->allocating_thread); vm_page_set_state(memory->pages[i], PAGE_STATE_FREE); } free(memory->pages); memory->pages = NULL; } #else delete_area(memory->area); memory->area = -1; #endif memory->flags &= ~ALLOCATED_APERTURE; } void Aperture::_Remove(aperture_memory *memory) { aperture_memory *current = fFirstMemory, *last = NULL; while (current != NULL) { if (memory == current) { if (last != NULL) { last->next = current->next; } else { fFirstMemory = current->next; } break; } last = current; current = current->next; } } status_t Aperture::_Insert(aperture_memory *memory, size_t size, size_t alignment, uint32 flags) { aperture_memory *last = NULL; aperture_memory *next; bool foundSpot = false; // do some sanity checking if (size == 0 || size > fInfo.size) return B_BAD_VALUE; if (alignment < B_PAGE_SIZE) alignment = B_PAGE_SIZE; addr_t start = fInfo.base; if ((flags & (B_APERTURE_NON_RESERVED | B_APERTURE_NEED_PHYSICAL)) != 0) start += fInfo.reserved_size; start = ROUNDUP(start, alignment); if (start > fInfo.base - 1 + fInfo.size || start < fInfo.base) return B_NO_MEMORY; // walk up to the spot where we should start searching next = fFirstMemory; while (next) { if (next->base >= start + size) { // we have a winner break; } last = next; next = next->next; } // find a big enough hole if (last == NULL) { // see if we can build it at the beginning of the virtual map if (next == NULL || (next->base >= ROUNDUP(start, alignment) + size)) { memory->base = ROUNDUP(start, alignment); foundSpot = true; } else { last = next; next = next->next; } } if (!foundSpot) { // keep walking while (next != NULL) { if (next->base >= ROUNDUP(last->base + last->size, alignment) + size) { // we found a spot (it'll be filled up below) break; } last = next; next = next->next; } if ((fInfo.base + (fInfo.size - 1)) >= (ROUNDUP(last->base + last->size, alignment) + (size - 1))) { // got a spot foundSpot = true; memory->base = ROUNDUP(last->base + last->size, alignment); if (memory->base < start) memory->base = start; } if (!foundSpot) return B_NO_MEMORY; } memory->size = size; if (last) { memory->next = last->next; last->next = memory; } else { memory->next = fFirstMemory; fFirstMemory = memory; } return B_OK; } // #pragma mark - AGP module interface status_t get_nth_agp_info(uint32 index, agp_info *info) { TRACE("get_nth_agp_info(index %" B_PRIu32 ")\n", index); if (index >= sDeviceCount) return B_BAD_VALUE; // refresh from the contents of the AGP registers from this device sDeviceInfos[index].info.interface.status = get_pci_config( sDeviceInfos[index].info, AGP_STATUS(sDeviceInfos[index].address), 4); sDeviceInfos[index].info.interface.command = get_pci_config( sDeviceInfos[index].info, AGP_COMMAND(sDeviceInfos[index].address), 4); *info = sDeviceInfos[index].info; return B_OK; } status_t acquire_agp(void) { if (atomic_or(&sAcquired, 1) == 1) return B_BUSY; return B_OK; } void release_agp(void) { atomic_and(&sAcquired, 0); } uint32 set_agp_mode(uint32 command) { TRACE("set_agp_mode(command %" B_PRIx32 ")\n", command); if ((command & AGP_ENABLE) == 0) { set_pci_mode(); return 0; } // Make sure we accept all modes lower than requested one and we // reset reserved bits command = fix_rate_support(command); // iterate through our device list to find the common capabilities supported for (uint32 index = 0; index < sDeviceCount; index++) { agp_device_info &deviceInfo = sDeviceInfos[index]; // Refresh from the contents of the AGP capability registers // (note: some graphics driver may have been tweaking, like nvidia) deviceInfo.info.interface.status = get_pci_config(deviceInfo.info, AGP_STATUS(deviceInfo.address), 4); check_capabilities(deviceInfo, command); } command = fix_rate_command(command); TRACE("set AGP command %" B_PRIx32 " on all capable devices.\n", command); // The order of programming differs for enabling/disabling AGP mode // (see AGP specification) // First program all bridges (master) for (uint32 index = 0; index < sDeviceCount; index++) { agp_device_info &deviceInfo = sDeviceInfos[index]; if (deviceInfo.info.class_base != PCI_bridge) continue; set_agp_command(deviceInfo, command); } // Wait 10mS for the bridges to recover (failsafe, see set_pci_mode()!) snooze(10000); // Then all graphics cards (target) for (uint32 index = 0; index < sDeviceCount; index++) { agp_device_info &deviceInfo = sDeviceInfos[index]; if (deviceInfo.info.class_base != PCI_display) continue; set_agp_command(deviceInfo, command); } return command; } // #pragma mark - GART module interface static aperture_id map_aperture(uint8 bus, uint8 device, uint8 function, size_t size, addr_t *_apertureBase) { void *iterator = open_module_list("busses/agp_gart"); status_t status = B_ENTRY_NOT_FOUND; Aperture *aperture = NULL; Autolock _(sLock); while (true) { char name[256]; size_t nameLength = sizeof(name); if (read_next_module_name(iterator, name, &nameLength) != B_OK) break; agp_gart_bus_module_info *module; if (get_module(name, (module_info **)&module) == B_OK) { void *privateAperture; status = module->create_aperture(bus, device, function, size, &privateAperture); if (status < B_OK) { put_module(name); continue; } aperture = new(std::nothrow) Aperture(module, privateAperture); status = aperture->InitCheck(); if (status == B_OK) { if (_apertureBase != NULL) *_apertureBase = aperture->Base(); sApertureHashTable.Insert(aperture); } else { delete aperture; aperture = NULL; } break; } } close_module_list(iterator); return aperture != NULL ? aperture->ID() : status; } static aperture_id map_custom_aperture(gart_bus_module_info *module, addr_t *_apertureBase) { return B_ERROR; } static status_t unmap_aperture(aperture_id id) { Autolock _(sLock); Aperture *aperture = sApertureHashTable.Lookup(id); if (aperture == NULL) return B_ENTRY_NOT_FOUND; sApertureHashTable.Remove(aperture); delete aperture; return B_OK; } static status_t get_aperture_info(aperture_id id, aperture_info *info) { Aperture *aperture = get_aperture(id); if (aperture == NULL) return B_ENTRY_NOT_FOUND; Autolock _(aperture->Lock()); return aperture->GetInfo(info); } static status_t allocate_memory(aperture_id id, size_t size, size_t alignment, uint32 flags, addr_t *_apertureBase, phys_addr_t *_physicalBase) { if ((flags & ~APERTURE_PUBLIC_FLAGS_MASK) != 0 || _apertureBase == NULL) return B_BAD_VALUE; Aperture *aperture = get_aperture(id); if (aperture == NULL) return B_ENTRY_NOT_FOUND; size = ROUNDUP(size, B_PAGE_SIZE); Autolock _(aperture->Lock()); aperture_memory *memory = aperture->CreateMemory(size, alignment, flags); if (memory == NULL) return B_NO_MEMORY; status_t status = aperture->AllocateMemory(memory, flags); if (status == B_OK) status = aperture->BindMemory(memory, 0, 0); if (status < B_OK) { aperture->DeleteMemory(memory); return status; } if (_physicalBase != NULL && (flags & B_APERTURE_NEED_PHYSICAL) != 0) { #if !defined(GART_TEST) *_physicalBase = (phys_addr_t)memory->page->physical_page_number * B_PAGE_SIZE; #else physical_entry entry; status = get_memory_map((void *)memory->base, B_PAGE_SIZE, &entry, 1); if (status < B_OK) { aperture->DeleteMemory(memory); return status; } *_physicalBase = entry.address; #endif } *_apertureBase = memory->base; return B_OK; } static status_t free_memory(aperture_id id, addr_t base) { Aperture *aperture = get_aperture(id); if (aperture == NULL) return B_ENTRY_NOT_FOUND; Autolock _(aperture->Lock()); aperture_memory *memory = aperture->GetMemory(base); if (memory == NULL) return B_BAD_VALUE; aperture->DeleteMemory(memory); return B_OK; } static status_t reserve_aperture(aperture_id id, size_t size, addr_t *_apertureBase) { Aperture *aperture = get_aperture(id); if (aperture == NULL) return B_ENTRY_NOT_FOUND; return B_ERROR; } static status_t unreserve_aperture(aperture_id id, addr_t apertureBase) { Aperture *aperture = get_aperture(id); if (aperture == NULL) return B_ENTRY_NOT_FOUND; return B_ERROR; } static status_t bind_aperture(aperture_id id, area_id area, addr_t base, size_t size, size_t alignment, addr_t reservedBase, addr_t *_apertureBase) { Aperture *aperture = get_aperture(id); if (aperture == NULL) return B_ENTRY_NOT_FOUND; if (area < 0) { if (size == 0 || size > aperture->Size() || (base & (B_PAGE_SIZE - 1)) != 0 || base == 0) return B_BAD_VALUE; size = ROUNDUP(size, B_PAGE_SIZE); } if (area >= 0) { status_t status = get_area_base_and_size(area, base, size); if (status < B_OK) return status; } Autolock _(aperture->Lock()); aperture_memory *memory = NULL; if (reservedBase != 0) { // use reserved aperture to bind the pages memory = aperture->GetMemory(reservedBase); if (memory == NULL) return B_BAD_VALUE; } else { // create new memory object memory = aperture->CreateMemory(size, alignment, B_APERTURE_NON_RESERVED); if (memory == NULL) return B_NO_MEMORY; } // just bind the physical pages backing the memory into the GART status_t status = aperture->BindMemory(memory, base, size); if (status < B_OK) { if (reservedBase != 0) aperture->DeleteMemory(memory); return status; } if (_apertureBase != NULL) *_apertureBase = memory->base; return B_OK; } static status_t unbind_aperture(aperture_id id, addr_t base) { Aperture *aperture = get_aperture(id); if (aperture == NULL) return B_ENTRY_NOT_FOUND; Autolock _(aperture->Lock()); aperture_memory *memory = aperture->GetMemory(base); if (memory == NULL || (memory->flags & BIND_APERTURE) == 0) return B_BAD_VALUE; if ((memory->flags & ALLOCATED_APERTURE) != 0) panic("unbind memory %lx (%p) allocated by agp_gart.", base, memory); status_t status = aperture->UnbindMemory(memory); if (status < B_OK) return status; if ((memory->flags & RESERVED_APERTURE) == 0) aperture->DeleteMemory(memory); return B_OK; } // #pragma mark - static status_t agp_init(void) { TRACE("bus manager init\n"); if (get_module(B_PCI_MODULE_NAME, (module_info **)&sPCI) != B_OK) return B_ERROR; uint32 cookie = 0; sDeviceCount = 0; pci_info info; while (get_next_agp_device(&cookie, info, sDeviceInfos[sDeviceCount]) == B_OK) { sDeviceCount++; } TRACE("found %" B_PRId32 " AGP devices\n", sDeviceCount); // Since there can be custom aperture modules (for memory management only), // we always succeed if we could get the resources we need. new(&sApertureHashTable) ApertureHashTable(); return init_lock(&sLock, "agp_gart"); } void agp_uninit(void) { TRACE("bus manager uninit\n"); ApertureHashTable::Iterator iterator = sApertureHashTable.GetIterator(); while (iterator.HasNext()) { Aperture *aperture = iterator.Next(); sApertureHashTable.Remove(aperture); delete aperture; } put_module(B_PCI_MODULE_NAME); } static int32 agp_std_ops(int32 op, ...) { switch (op) { case B_MODULE_INIT: return agp_init(); case B_MODULE_UNINIT: agp_uninit(); return B_OK; } return B_BAD_VALUE; } static struct agp_gart_module_info sAGPModuleInfo = { { { B_AGP_GART_MODULE_NAME, B_KEEP_LOADED, // Keep loaded, even if no driver requires it agp_std_ops }, NULL // the rescan function }, get_nth_agp_info, acquire_agp, release_agp, set_agp_mode, map_aperture, map_custom_aperture, unmap_aperture, get_aperture_info, allocate_memory, free_memory, reserve_aperture, unreserve_aperture, bind_aperture, unbind_aperture, }; module_info *modules[] = { (module_info *)&sAGPModuleInfo, NULL };