#include <AGP.h>
#include <stdlib.h>
#include <KernelExport.h>
#include <PCI.h>
#include <util/OpenHashTable.h>
#include <kernel/lock.h>
#include <vm/vm_page.h>
#include <vm/vm_types.h>
#include <lock.h>
#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)
#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<MemoryHashDefinition> MemoryHashTable;
struct agp_device_info {
uint8 address;
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<ApertureHashDefinition> 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;
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;
} else {
if ((command & AGP_2_4x) != 0)
command |= AGP_2_2x;
if ((command & AGP_2_2x) != 0)
command |= AGP_2_1x;
}
return command;
}
static uint32
fix_rate_command(uint32 command)
{
if ((command & AGP_3_MODE) != 0) {
if ((command & AGP_3_8x) != 0)
command &= ~AGP_3_4x;
} else {
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;
}
static void
check_capabilities(agp_device_info &deviceInfo, uint32 &command)
{
uint32 agpStatus = deviceInfo.info.interface.status;
if (deviceInfo.info.class_base == PCI_bridge) {
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);
command &= (agpStatus & (AGP_3_MODE | AGP_RATE_MASK))
| ~(AGP_3_MODE | AGP_RATE_MASK);
if ((agpStatus & AGP_RATE_MASK) == 0)
command = 0;
if ((agpStatus & AGP_SBA) == 0)
command &= ~AGP_SBA;
if ((agpStatus & AGP_FAST_WRITE) == 0)
command &= ~AGP_FAST_WRITE;
uint8 requestDepth = ((agpStatus & AGP_REQUEST) >> AGP_REQUEST_SHIFT);
if (requestDepth < ((command & AGP_REQUEST) >> AGP_REQUEST_SHIFT)) {
command &= ~AGP_REQUEST;
command |= (requestDepth << AGP_REQUEST_SHIFT);
}
}
static bool
is_agp_device(pci_info &info, uint8 *_address)
{
if ((get_pci_config(info, PCI_status, 2) & PCI_status_capabilities) == 0)
return false;
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) {
if (_address != NULL)
*_address = address;
return true;
}
if (next == 0) {
break;
}
address = next;
}
return false;
}
static status_t
get_next_agp_device(uint32 *_cookie, pci_info &info, agp_device_info &device)
{
uint32 index = *_cookie;
for (; sPCI->get_nth_pci_info(index, &info) == B_OK; index++) {
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;
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");
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);
}
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);
}
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);
}
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)
{
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;
#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 {
memory->pages = (vm_page **)malloc(count * sizeof(vm_page *));
if (memory->pages == NULL)
return B_NO_MEMORY;
#if B_HAIKU_PHYSICAL_BITS > 32
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
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;
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) {
size = memory->size;
physical = true;
}
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)
size_t size = memory->size;
addr_t reservedEnd = fInfo.base + fInfo.reserved_size;
if (memory->base < reservedEnd)
size -= reservedEnd - memory->base;
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_free(NULL, page);
}
memory->page = NULL;
} else {
for (uint32 i = 0; i < count; i++) {
DEBUG_PAGE_ACCESS_TRANSFER(memory->pages[i],
memory->allocating_thread);
vm_page_free(NULL, memory->pages[i]);
}
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;
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;
next = fFirstMemory;
while (next) {
if (next->base >= start + size) {
break;
}
last = next;
next = next->next;
}
if (last == NULL) {
if (next == NULL || (next->base >= ROUNDUP(start, alignment) + size)) {
memory->base = ROUNDUP(start, alignment);
foundSpot = true;
} else {
last = next;
next = next->next;
}
}
if (!foundSpot) {
while (next != NULL) {
if (next->base >= ROUNDUP(last->base + last->size, alignment) + size) {
break;
}
last = next;
next = next->next;
}
if ((fInfo.base + (fInfo.size - 1)) >= (ROUNDUP(last->base + last->size,
alignment) + (size - 1))) {
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;
}
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;
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;
}
command = fix_rate_support(command);
for (uint32 index = 0; index < sDeviceCount; index++) {
agp_device_info &deviceInfo = sDeviceInfos[index];
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);
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);
}
snooze(10000);
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;
}
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) {
memory = aperture->GetMemory(reservedBase);
if (memory == NULL)
return B_BAD_VALUE;
} else {
memory = aperture->CreateMemory(size, alignment,
B_APERTURE_NON_RESERVED);
if (memory == NULL)
return B_NO_MEMORY;
}
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;
}
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);
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,
agp_std_ops
},
NULL
},
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
};