0ba91ae22a
With some newer UEFI firmware, not all pci bars get programmed and we have to assign them ourselfs. This was already done for memory bars. This change adds the same for i/o port space, by providing a ioreservewin() function which can be used to allocate port space within the parent pci-pci bridge window. Also, the pci code now allocates the pci config space i/o ports 0xCF8/0xCFC so userspace needs to use devpnp to access pci config space now. (see latest realemu change). Also, this moves the ioalloc()/iofree() code out of devarch into port/iomap.c as it can be shared with the ppc mtx kernel.
244 lines
4.4 KiB
C
244 lines
4.4 KiB
C
#include "u.h"
|
|
#include "../port/lib.h"
|
|
#include "mem.h"
|
|
#include "dat.h"
|
|
#include "fns.h"
|
|
|
|
typedef struct IOMap IOMap;
|
|
struct IOMap
|
|
{
|
|
IOMap *next;
|
|
ulong start;
|
|
ulong end;
|
|
char reserved;
|
|
char tag[13];
|
|
};
|
|
|
|
static struct
|
|
{
|
|
Lock;
|
|
IOMap *m;
|
|
IOMap *free;
|
|
IOMap maps[32]; /* some initial free maps */
|
|
|
|
ulong mask;
|
|
} iomap;
|
|
|
|
static void
|
|
insert(IOMap **l, ulong start, ulong end, char *tag, int reserved)
|
|
{
|
|
IOMap *m;
|
|
|
|
m = iomap.free;
|
|
if(m != nil)
|
|
iomap.free = m->next;
|
|
else
|
|
m = malloc(sizeof(IOMap));
|
|
if(m == nil)
|
|
panic("ioalloc: out of memory");
|
|
|
|
m->next = *l;
|
|
m->start = start;
|
|
m->end = end;
|
|
m->reserved = reserved;
|
|
|
|
strncpy(m->tag, tag, sizeof(m->tag)-1);
|
|
m->tag[sizeof(m->tag)-1] = 0;
|
|
|
|
*l = m;
|
|
}
|
|
|
|
/*
|
|
* Reserve a range to be ioalloced later.
|
|
* If port is -1, look for a free range above 0x400.
|
|
*/
|
|
int
|
|
ioreserve(ulong port, ulong size, ulong align, char *tag)
|
|
{
|
|
if(port == -1)
|
|
return ioreservewin(0x400, -0x400 & iomap.mask, size, align, tag);
|
|
else
|
|
return ioreservewin(port, size, size, align, tag);
|
|
}
|
|
|
|
/*
|
|
* Find a free region of length "size" within the window [port, port+win)
|
|
* and reserve it to be ioalloced later.
|
|
*/
|
|
int
|
|
ioreservewin(ulong port, ulong win, ulong size, ulong align, char *tag)
|
|
{
|
|
IOMap *m, **l;
|
|
|
|
if(win == 0 || port & ~iomap.mask || (~port & iomap.mask) < (win-1 & iomap.mask))
|
|
return -1;
|
|
|
|
size = (size + ~iomap.mask) & iomap.mask;
|
|
if(size == 0 || size > win)
|
|
return -1;
|
|
|
|
if(align){
|
|
if((align & (align-1)) != 0)
|
|
return -1;
|
|
align--;
|
|
}
|
|
|
|
win += port;
|
|
port = (port+align) & ~align;
|
|
if(port >= win || win - port < size)
|
|
return -1;
|
|
|
|
lock(&iomap);
|
|
for(l = &iomap.m; (m = *l) != nil; l = &m->next){
|
|
if(m->end <= port)
|
|
continue;
|
|
if(m->start > port && m->start - port >= size)
|
|
break;
|
|
port = m->end;
|
|
port = (port+align) & ~align;
|
|
if(port >= win || win - port < size){
|
|
unlock(&iomap);
|
|
return -1;
|
|
}
|
|
}
|
|
insert(l, port, port + size, tag, 1);
|
|
unlock(&iomap);
|
|
|
|
return port;
|
|
}
|
|
|
|
/*
|
|
* Alloc some io port space and remember who it was
|
|
* alloced to. If port == -1, find a free region.
|
|
*/
|
|
int
|
|
ioalloc(ulong port, ulong size, ulong align, char *tag)
|
|
{
|
|
IOMap *m, **l;
|
|
|
|
if(port == -1)
|
|
port = ioreserve(port, size, align, tag);
|
|
|
|
size = (size + ~iomap.mask) & iomap.mask;
|
|
if(size == 0 || port & ~iomap.mask || (~port & iomap.mask) < (size-1 & iomap.mask))
|
|
return -1;
|
|
|
|
lock(&iomap);
|
|
for(l = &iomap.m; (m = *l) != nil; l = &m->next){
|
|
if(m->end <= port)
|
|
continue;
|
|
if(m->start > port && m->start - port >= size)
|
|
break;
|
|
if(m->reserved && m->start <= port && m->end - port >= size){
|
|
if(m->end - port > size)
|
|
insert(&m->next, port + size, m->end, m->tag, 1);
|
|
if(m->start < port){
|
|
insert(l, m->start, port, m->tag, 1);
|
|
l = &(*l)->next;
|
|
}
|
|
*l = m->next;
|
|
m->next = iomap.free;
|
|
iomap.free = m;
|
|
break;
|
|
}
|
|
print("ioalloc: %lux - %lux %s: clashes with: %lux - %lux %s\n",
|
|
port, port+size-1, tag,
|
|
m->start, m->end-1, m->tag);
|
|
unlock(&iomap);
|
|
return -1;
|
|
}
|
|
insert(l, port, port + size, tag, 0);
|
|
unlock(&iomap);
|
|
|
|
return port;
|
|
}
|
|
|
|
void
|
|
iofree(ulong port)
|
|
{
|
|
IOMap *m, **l;
|
|
|
|
if(port & ~iomap.mask)
|
|
return;
|
|
|
|
lock(&iomap);
|
|
for(l = &iomap.m; (m = *l) != nil; l = &m->next){
|
|
if(m->start == port){
|
|
*l = m->next;
|
|
m->next = iomap.free;
|
|
iomap.free = m;
|
|
break;
|
|
}
|
|
if(m->start > port)
|
|
break;
|
|
}
|
|
unlock(&iomap);
|
|
}
|
|
|
|
int
|
|
iounused(ulong start, ulong end)
|
|
{
|
|
IOMap *m;
|
|
|
|
if(start & ~iomap.mask || end < start)
|
|
return 0;
|
|
|
|
for(m = iomap.m; m != nil; m = m->next){
|
|
if(m->end <= start)
|
|
continue;
|
|
if(m->start >= end)
|
|
break;
|
|
if(!m->reserved)
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
static long
|
|
iomapread(Chan*, void *a, long n, vlong offset)
|
|
{
|
|
char buf[32];
|
|
IOMap *m;
|
|
int i;
|
|
|
|
lock(&iomap);
|
|
i = 0;
|
|
for(m = iomap.m; m != nil; m = m->next){
|
|
i = snprint(buf, sizeof(buf), "%8lux %8lux %-12.12s\n",
|
|
m->start, m->end-1, m->tag);
|
|
offset -= i;
|
|
if(offset < 0)
|
|
break;
|
|
}
|
|
unlock(&iomap);
|
|
if(offset >= 0)
|
|
return 0;
|
|
if(n > -offset)
|
|
n = -offset;
|
|
offset += i;
|
|
memmove(a, buf+offset, n);
|
|
return n;
|
|
}
|
|
|
|
/*
|
|
* Initialize the io space port map.
|
|
* The mask argument defines the valid bits of
|
|
* a port address, so different architectures
|
|
* might have different sizes and alignments.
|
|
*/
|
|
void
|
|
iomapinit(ulong mask)
|
|
{
|
|
int i;
|
|
|
|
assert(mask != 0 && (mask >> 31) == 0);
|
|
|
|
for(i = 0; i < nelem(iomap.maps)-1; i++)
|
|
iomap.maps[i].next = &iomap.maps[i+1];
|
|
iomap.maps[i].next = nil;
|
|
iomap.free = iomap.maps;
|
|
iomap.mask = mask;
|
|
|
|
addarchfile("ioalloc", 0444, iomapread, nil);
|
|
}
|