Building the new src tree

This commit is contained in:
Ronald G. Minnich 2000-10-16 03:03:03 +00:00
commit 96fa389618
9 changed files with 5099 additions and 0 deletions

215
src/cpu/p5/cpuid.c Normal file
View file

@ -0,0 +1,215 @@
#include "intel_conf.h"
#include "intel_subr.h"
#include "printk.h"
#ifdef i586
#include <asm/msr.h>
#endif
#ifdef i586
int intel_mtrr_check(void)
{
unsigned long low, high;
printk(KERN_INFO "\nMTRR check\n");
rdmsr(0x2ff, low, high);
low = low >> 10;
printk(KERN_INFO "Fixed MTRRs : ");
if (low & 0x01)
printk(KERN_INFO "Enabled\n");
else
printk(KERN_INFO "Disabled\n");
printk(KERN_INFO "Variable MTRRs: ");
if (low & 0x02)
printk(KERN_INFO "Enabled\n");
else
printk(KERN_INFO "Disabled\n");
printk(KERN_INFO "\n");
return ((int) low);
}
#endif
void intel_display_cpuid(void)
{
int op, eax, ebx, ecx, edx;
int max_op;
max_op = 0;
printk(KERN_INFO "\n");
for (op = 0; op <= max_op; op++) {
intel_cpuid(op, &eax, &ebx, &ecx, &edx);
if (0 == op) {
max_op = eax;
printk(KERN_INFO "Max cpuid index : %d\n", eax);
printk(KERN_INFO "Vendor ID : "
"%c%c%c%c%c%c%c%c%c%c%c%c\n",
ebx, ebx >> 8, ebx >> 16, ebx >> 24, edx,
edx >> 8, edx >> 16, edx >> 24, ecx, ecx >> 8,
ecx >> 16, ecx >> 24);
} else if (1 == op) {
printk(KERN_INFO "Processor Type : 0x%02x\n",
(eax >> 12) & 0x03);
printk(KERN_INFO "Processor Family : 0x%02x\n",
(eax >> 8) & 0x0f);
printk(KERN_INFO "Processor Model : 0x%02x\n",
(eax >> 4) & 0x0f);
printk(KERN_INFO "Processor Mask : 0x%02x\n",
(ecx >> 0) & 0x0f);
printk(KERN_INFO "Processor Stepping : 0x%02x\n",
(eax >> 0) & 0x0f);
printk(KERN_INFO "Feature flags : 0x%08x\n", edx);
} else if (2 == op) {
int desc[4];
int ii;
int _desc;
printk(KERN_INFO "\n");
printk(KERN_INFO "Cache/TLB descriptor values: %d "
"reads required\n", eax & 0xff);
desc[0] = eax;
desc[1] = ebx;
desc[2] = ecx;
desc[3] = edx;
for (ii = 1; ii < 16; ii++) {
if (desc[ii >> 2] & 0x80000000) {
printk(KERN_INFO
"reserved descriptor\n");
continue;
}
_desc =
((desc[ii>>2]) >> ((ii & 0x3) << 3)) & 0xff;
printk(KERN_INFO "Desc 0x%02x : ", _desc);
switch (_desc) {
case 0x00:
printk(KERN_INFO "null\n");
break;
case 0x01:
printk(KERN_INFO "Instr TLB: "
"4KB pages, "
"4-way set assoc, "
"32 entries\n");
break;
case 0x02:
printk(KERN_INFO "Instr TLB: "
"4MB pages, "
"fully assoc, "
"2 entries\n");
break;
case 0x03:
printk(KERN_INFO "Data TLB: "
"4KB pages, "
"4-way set assoc, "
"64 entries\n");
break;
case 0x04:
printk(KERN_INFO "Data TLB: "
"4MB pages, "
"4-way set assoc, "
"8 entries\n");
break;
case 0x06:
printk(KERN_INFO "Inst cache: "
"8K bytes, "
"4-way set assoc, "
"32 byte line size\n");
break;
case 0x08:
printk(KERN_INFO "Inst cache: "
"16K bytes, "
"4-way set assoc, "
"32 byte line size\n");
break;
case 0x0a:
printk(KERN_INFO "Data cache: "
"8K bytes, "
"2-way set assoc, "
"32 byte line size\n");
break;
case 0x0c:
printk(KERN_INFO "Data cache: "
"16K bytes, "
"2-way or 4-way set assoc, "
"32 byte line size\n");
break;
case 0x40:
printk(KERN_INFO "No L2 cache\n");
break;
case 0x41:
printk(KERN_INFO "L2 Unified cache: "
"128K bytes, "
"4-way set assoc, "
"32 byte line size\n");
break;
case 0x42:
printk(KERN_INFO "L2 Unified cache: "
"256K bytes, "
"4-way set assoc, "
"32 byte line size\n");
break;
case 0x43:
printk(KERN_INFO "L2 Unified cache: "
"512K bytes, "
"4-way set assoc, "
"32 byte line size\n");
break;
case 0x44:
printk(KERN_INFO "L2 Unified cache: "
"1M byte, "
"4-way set assoc, "
"32 byte line size\n");
break;
case 0x45:
printk(KERN_INFO "L2 Unified cache: "
"2M byte, "
"4-way set assoc, "
"32 byte line size\n");
break;
case 0x82:
printk(KERN_INFO "L2 Unified cache: "
"256K bytes, "
"8-way set assoc, "
"32 byte line size\n");
break;
default:
printk(KERN_INFO "UNKNOWN\n");
}
}
printk(KERN_INFO "\n");
} else {
printk(KERN_INFO "op: 0x%02x eax:0x%08x "
"ebx:0x%08x ecx:0x%08x edx:0x%08x\n",
op, eax, ebx, ecx, edx);
}
}
printk(KERN_INFO "\n");
}

35
src/lib/definitions.h Normal file
View file

@ -0,0 +1,35 @@
/* Comment this out unless you are debugging under linux */
#define EMULATE
#undef EMULATE
#ifdef EMULATE
#include <unistd.h>
#else
typedef unsigned long size_t;
#endif
#ifdef EMULATE
#define MEMSIZE 2*1024*1024 /* Make a 2MB fake memory space */
char memimage[MEMSIZE]; /* Make a 2MB fake memory space */
#else
#define memimage 0x0 /* Ignore memimage */
#endif
#define KERNEL_START (0x100000 + memimage) /* Put our copy of linux here */
#if 0
#define ZIP_START (0x30000 + memimage) /* The zip file starts here */
#define ZIP_SIZE 262164 /* linux.gz size (we ought to */
#endif
/* with flash, it's a bunch of 64k segments. */
#define ZIP_START (0xfff40000)
#define ZIP_SIZE (0x10000)
#ifdef EMULATE
char input_array[0x100000];
#undef ZIP_START
#define ZIP_START (input_array + 0x40000)
#endif
/* make this dynamic) */

1214
src/lib/inflate.c Normal file

File diff suppressed because it is too large Load diff

1579
src/lib/linuxbiosmain.c Normal file

File diff suppressed because it is too large Load diff

478
src/lib/linuxpci.c Normal file
View file

@ -0,0 +1,478 @@
/*
* $Id$
*
* PCI Bus Services, see include/linux/pci.h for further explanation.
*
* Copyright 1993 -- 1997 Drew Eckhardt, Frederic Potter,
* David Mosberger-Tang
*
* Copyright 1997 -- 1999 Martin Mares <mj@atrey.karlin.mff.cuni.cz>
*/
/* the intent of this file is to easily copy any new pci.c from the
* linux source tree, so keep your mods to a minimum, please
* RGM
*/
#include <lbpci.h>
#include <asm/io.h>
extern void intel_post(unsigned char value);
#define DEBUG
#ifdef DEBUG
#define DBG(x...) printk(KERN_DEBUG x)
#else
#define DBG(x...)
#endif
/**
* This is the root of the PCI tree. A PCI tree always has
* one bus, bus 0. Bus 0 contains devices and bridges.
*/
struct pci_bus pci_root;
/// Linked list of PCI devices. ALL devices are on this list
struct pci_dev *pci_devices = 0;
/// pointer to the last device */
static struct pci_dev **pci_last_dev_p = &pci_devices;
/// We're going to probably delete this -- flag to add in reverse order */
static int pci_reverse = 0;
/**
* Given a bus and a devfn number, find the device structure
* @param bus The bus number
* @param devfn a device/function number
* @return pointer to the device structure
*/
struct pci_dev *pci_find_slot(unsigned int bus, unsigned int devfn)
{
struct pci_dev *dev;
for (dev = pci_devices; dev; dev = dev->next)
if (dev->bus->number == bus && dev->devfn == devfn)
break;
return dev;
}
/** Find a device of a given vendor and type
* @param vendor Vendor ID (e.g. 0x8086 for Intel)
* @param device Device ID
* @param from Pointer to the device structure, used as a starting point
* in the linked list of devices, which can be 0 to start at the
* head of the list (i.e. pci_devices)
* @return Pointer to the device struct
*/
struct pci_dev *pci_find_device(unsigned int vendor, unsigned int device, struct pci_dev *from)
{
if (!from)
from = pci_devices;
else
from = from->next;
while (from && (from->vendor != vendor || from->device != device))
from = from->next;
return from;
}
/** Find a device of a given class
* @param class Class of the device
* @param from Pointer to the device structure, used as a starting point
* in the linked list of devices, which can be 0 to start at the
* head of the list (i.e. pci_devices)
* @return Pointer to the device struct
*/
struct pci_dev *pci_find_class(unsigned int class, struct pci_dev *from)
{
if (!from)
from = pci_devices;
else
from = from->next;
while (from && from->class != class)
from = from->next;
return from;
}
/** Given a device, set the PCI_COMMAND_MASTER bit in the command register
* @param dev Pointer to the device structure
*/
void pci_set_master(struct pci_dev *dev)
{
u16 cmd;
u8 lat;
pci_read_config_word(dev, PCI_COMMAND, &cmd);
if (!(cmd & PCI_COMMAND_MASTER)) {
printk("PCI: Enabling bus mastering for device %02x:%02x\n",
dev->bus->number, dev->devfn);
cmd |= PCI_COMMAND_MASTER;
pci_write_config_word(dev, PCI_COMMAND, cmd);
}
pci_read_config_byte(dev, PCI_LATENCY_TIMER, &lat);
if (lat < 16) {
printk("PCI: Increasing latency timer of device %02x:%02x to 64\n",
dev->bus->number, dev->devfn);
pci_write_config_byte(dev, PCI_LATENCY_TIMER, 64);
}
}
/** Given a device and register, read the size of the BAR for that register.
* @param dev Pointer to the device structure
* @param reg Which register to use
* @param addr Address to load into the register after size is found
*/
void pci_get_size(struct pci_dev *dev, unsigned long reg, unsigned long addr)
{
u32 size;
unsigned long type;
/* FIXME: more consideration for 64-bit PCI devices */
// get the size
pci_write_config_dword(dev, PCI_BASE_ADDRESS_0 + (reg << 2), ~0);
pci_read_config_dword(dev, PCI_BASE_ADDRESS_0 + (reg << 2), &size);
// restore addr
pci_write_config_dword(dev, PCI_BASE_ADDRESS_0 + (reg << 2), addr);
// Now compute the actual size, See PCI Spec 6.2.5.1 ...
if (size & PCI_BASE_ADDRESS_SPACE_IO) {
type = size & (~PCI_BASE_ADDRESS_IO_MASK);
size &= (PCI_BASE_ADDRESS_IO_MASK);
// BUG! Top 16 bits can be zero (or not)
// So set them to 0xffff so they go away ...
size |= 0xffff0000;
size = ~size;
size++;
} else {
type = size & (~PCI_BASE_ADDRESS_MEM_MASK);
size &= (PCI_BASE_ADDRESS_MEM_MASK);
size = ~size;
size++;
}
dev->size[reg] = size | type;
}
/** Read the base address registers for a given device.
* @param dev Pointer to the dev structure
* @param howmany How many registers to read (6 for device, 2 for bridge)
*/
void pci_read_bases(struct pci_dev *dev, unsigned int howmany)
{
unsigned int reg;
u32 /* unsigned long for 64 bits ?? */ addr;
/* FIXME: to deal with 64-bits PCI */
for (reg = 0; reg < howmany; reg++) {
pci_read_config_dword(dev, PCI_BASE_ADDRESS_0 + (reg << 2), &addr);
if (addr == 0xffffffff)
continue;
/* get address space size */
pci_get_size(dev, reg, addr);
addr &= (PCI_BASE_ADDRESS_SPACE | PCI_BASE_ADDRESS_MEM_TYPE_MASK);
if (addr == (PCI_BASE_ADDRESS_SPACE_MEMORY | PCI_BASE_ADDRESS_MEM_TYPE_64)) {
/* this is a 64-bit memory base address */
reg++;
pci_read_config_dword(dev, PCI_BASE_ADDRESS_0 + (reg << 2), &addr);
if (addr) {
#if BITS_PER_LONG == 64
dev->base_address[reg - 1] |= ((unsigned long) addr) << 32;
#else
printk("PCI: Unable to handle 64-bit address for device "
"%02x:%02x\n", dev->bus->number, dev->devfn);
dev->base_address[reg - 1] = 0;
#endif
}
}
}
}
/** Scan the bus, first for bridges and next for devices.
* @param pci_bus pointer to the bus structure
* @return The maximum bus number found, after scanning all subordinate busses
*/
unsigned int pci_scan_bus(struct pci_bus *bus)
{
unsigned int devfn, max;
struct pci_dev *dev, **bus_last;
struct pci_bus *child;
DBG("PCI: pci_scan_bus for bus %d\n", bus->number);
bus_last = &bus->devices;
max = bus->secondary;
intel_post(0x24);
/* probe all devices on this bus with some optimization for non-existance and
single funcion devices */
for (devfn = 0; devfn < 0xff; devfn++) {
u32 id, class, addr;
u8 cmd, tmp, hdr_type;
if (pcibios_read_config_dword(bus->number, devfn, PCI_VENDOR_ID, &id))
continue;
/* some broken boards return 0 if a slot is empty: */
if (id == 0xffffffff || id == 0x00000000 || id == 0x0000ffff || id == 0xffff0000) {
if (PCI_FUNC(devfn) == 0x00) {
/* if this is a function 0 device and it is not present,
skip to next device */
devfn += 0x07;
}
/* multi function device, skip to next function */
continue;
}
if (pcibios_read_config_byte(bus->number, devfn, PCI_HEADER_TYPE, &hdr_type))
continue;
if (pcibios_read_config_dword(bus->number, devfn, PCI_CLASS_REVISION, &class))
continue;
if ((dev = kmalloc(sizeof(*dev), GFP_ATOMIC)) == NULL) {
printk("PCI: out of memory.\n");
continue;
}
memset(dev, 0, sizeof(*dev));
dev->bus = bus;
dev->devfn = devfn;
dev->vendor = id & 0xffff;
dev->device = (id >> 16) & 0xffff;
dev->hdr_type = hdr_type;
/* class code, the upper 3 bytes of PCI_CLASS_REVISION */
dev->class = class >> 8;
class >>= 16;
/* non-destructively determine if device can be a master: */
pcibios_read_config_byte(bus->number, devfn, PCI_COMMAND, &cmd);
pcibios_write_config_byte(bus->number, devfn, PCI_COMMAND,
cmd | PCI_COMMAND_MASTER);
pcibios_read_config_byte(bus->number, devfn, PCI_COMMAND, &tmp);
dev->master = ((tmp & PCI_COMMAND_MASTER) != 0);
pcibios_write_config_byte(bus->number, devfn, PCI_COMMAND, cmd);
switch (hdr_type & 0x7f) { /* header type */
case PCI_HEADER_TYPE_NORMAL: /* standard header */
if (class == PCI_CLASS_BRIDGE_PCI)
goto bad;
/* read base address registers, again pci_fixup() can tweak these */
pci_read_bases(dev, 6);
pcibios_read_config_dword(bus->number, devfn, PCI_ROM_ADDRESS, &addr);
dev->rom_address = (addr == 0xffffffff) ? 0 : addr;
break;
case PCI_HEADER_TYPE_BRIDGE: /* bridge header */
if (class != PCI_CLASS_BRIDGE_PCI)
goto bad;
pci_read_bases(dev, 2);
pcibios_read_config_dword(bus->number, devfn, PCI_ROM_ADDRESS1, &addr);
dev->rom_address = (addr == 0xffffffff) ? 0 : addr;
break;
case PCI_HEADER_TYPE_CARDBUS: /* CardBus bridge header */
if (class != PCI_CLASS_BRIDGE_CARDBUS)
goto bad;
pci_read_bases(dev, 1);
break;
default: /* unknown header */
bad:
printk("PCI: %02x:%02x [%04x/%04x/%06x] has unknown header type %02x, "
"ignoring.\n",
bus->number, dev->devfn, dev->vendor, dev->device, class,
hdr_type);
continue;
}
DBG("PCI: %02x:%02x [%04x/%04x]\n", bus->number, dev->devfn,
dev->vendor, dev->device);
/* Put it into the global PCI device chain. It's used to find devices once
everything is set up. */
if (!pci_reverse) {
*pci_last_dev_p = dev;
pci_last_dev_p = &dev->next;
} else {
dev->next = pci_devices;
pci_devices = dev;
}
/* Now insert it into the list of devices held by the parent bus. */
*bus_last = dev;
bus_last = &dev->sibling;
if (PCI_FUNC(devfn) == 0x00 && (hdr_type & 0x80) != 0x80) {
/* if this is not a multi function device, don't waste time probe
another function. Skip to next device. */
devfn += 0x07;
}
}
intel_post(0x25);
/*
* After performing arch-dependent fixup of the bus, look behind
* all PCI-to-PCI bridges on this bus.
*/
//pcibios_fixup_bus(bus);
/*
* The fixup code may have just found some peer pci bridges on this
* machine. Update the max variable if that happened so we don't
* get duplicate bus numbers.
*/
for (child = &pci_root; child; child = child->next)
max = ((max > child->subordinate) ? max : child->subordinate);
for (dev = bus->devices; dev; dev = dev->sibling)
/* If it's a bridge, scan the bus behind it. */
if ((dev->class >> 8) == PCI_CLASS_BRIDGE_PCI) {
unsigned int buses;
unsigned int devfn = dev->devfn;
unsigned short cr;
#define NOTUSED
#ifdef NOTUSED
/*
* Check for a duplicate bus. If we already scanned
* this bus number as a peer bus, don't also scan it
* as a child bus
*/
if (((dev->vendor == PCI_VENDOR_ID_RCC) &&
((dev->device == PCI_DEVICE_ID_RCC_HE) ||
(dev->device == PCI_DEVICE_ID_RCC_LE))) ||
((dev->vendor == PCI_VENDOR_ID_COMPAQ) &&
(dev->device == PCI_DEVICE_ID_COMPAQ_6010)) ||
((dev->vendor == PCI_VENDOR_ID_INTEL) &&
((dev->device == PCI_DEVICE_ID_INTEL_82454NX)||
(dev->device == PCI_DEVICE_ID_INTEL_82451NX))))
goto skip_it;
/* Read the existing primary/secondary/subordinate bus number
configuration to determine if the PCI bridge has already been
configured by the system. If so, check to see if we've already
scanned this bus as a result of peer bus scanning, if so, skip this.
FIMXE: We are BIOS, is there anyone else doing this dirty job BEFORE us ?? */
pcibios_read_config_dword(bus->number, devfn, PCI_PRIMARY_BUS, &buses);
if ((buses & 0xFFFFFF) != 0) {
for (child = pci_root.next; child; child = child->next)
if (child->number == ((buses >> 8) & 0xff))
goto skip_it;
}
#endif
/* Insert it into the tree of buses. */
if ((child = kmalloc(sizeof(*child), GFP_ATOMIC)) == NULL) {
printk("PCI: out of memory for bridge.\n");
continue;
}
memset(child, 0, sizeof(*child));
child->next = bus->children;
bus->children = child;
child->self = dev;
child->parent = bus;
/* Set up the primary, secondary and subordinate bus numbers. We have
no idea how many buses are behind this bridge yet, so we set the
subordinate bus number to 0xff for the moment */
child->number = child->secondary = ++max;
child->primary = bus->secondary;
child->subordinate = 0xff;
/* Clear all status bits and turn off memory, I/O and master enables. */
pcibios_read_config_word(bus->number, devfn, PCI_COMMAND, &cr);
pcibios_write_config_word(bus->number, devfn, PCI_COMMAND, 0x0000);
pcibios_write_config_word(bus->number, devfn, PCI_STATUS, 0xffff);
/*
* Read the existing primary/secondary/subordinate bus
* number configuration to determine if the PCI bridge
* has already been configured by the system. If so,
* do not modify the configuration, merely note it.
*/
pcibios_read_config_dword(bus->number, devfn, PCI_PRIMARY_BUS, &buses);
#ifdef BRIDGE_CONFIGURED_AT_POWERUP
// There is some hardware (ALPHA) that configures bridges in hardware, at bootup.
// We need to take that into account at some point.
// At the same time, we're finding buggy bridge hardware that comes up
// with these registers non-zero (VIA VT8601). Hence this #ifdef -- in some cases,
// you should never check the buses; in other cases, you have no choice.
if ((buses & 0xFFFFFF) != 0) {
unsigned int cmax;
child->primary = buses & 0xFF;
child->secondary = (buses >> 8) & 0xFF;
child->subordinate = (buses >> 16) & 0xFF;
child->number = child->secondary;
cmax = pci_scan_bus(child);
if (cmax > max)
max = cmax;
} else
#endif
{
/* Configure the bus numbers for this bridge: the configuration
transactions will not be propagated by the bridge if it is not
correctly configured */
buses &= 0xff000000;
buses |= (((unsigned int) (child->primary) << 0) |
((unsigned int) (child->secondary) << 8) |
((unsigned int) (child->subordinate) << 16));
pcibios_write_config_dword(bus->number, devfn,
PCI_PRIMARY_BUS, buses);
/* Now we can scan all subordinate buses i.e. the bus hehind the bridge */
max = pci_scan_bus(child);
/* We know the number of buses behind this bridge. Set the subordinate
bus number to its real value */
child->subordinate = max;
buses = (buses & 0xff00ffff) |
((unsigned int) (child->subordinate) << 16);
pcibios_write_config_dword(bus->number, devfn,
PCI_PRIMARY_BUS, buses);
}
pcibios_write_config_word(bus->number, devfn, PCI_COMMAND, cr);
skip_it:
}
/*
* We've scanned the bus and so we know all about what's on
* the other side of any bridges that may be on this bus plus
* any devices.
*
* Return how far we've got finding sub-buses.
*/
DBG("PCI: pci_scan_bus returning with max=%02x\n", max);
intel_post(0x55);
return max;
}
/** Scan a peer bridge.
* First, scan all the bus structs for this bus number. If this bus
* is found, that means it was configured, so just return the pointer.
* If the bus is not found, it needs to be configured. Allocate memory
* for the struct, link it in to the linked list of bridges, and then
* scan it.
* @param bus The bus number supported by the peer bridge
* @return Pointer to the bus struct for this bus number.
struct pci_bus *pci_scan_peer_bridge(int bus)
{
struct pci_bus *b;
b = &pci_root;
while ((b != NULL) && (bus != 0)) {
if (b->number == bus)
return (b);
b = b->next;
}
b = kmalloc(sizeof(*b), GFP_KERNEL);
memset(b, 0, sizeof(*b));
b->next = pci_root.next;
pci_root.next = b;
b->number = b->secondary = bus;
b->subordinate = pci_scan_bus(b);
return b;
}
/** Initialize pci root struct, then scan starting at the root.
* Note that this function will recurse at each bridge.
*/
void pci_init(void)
{
memset(&pci_root, 0, sizeof(pci_root));
pci_root.subordinate = pci_scan_bus(&pci_root);
}

993
src/lib/newpci.c Normal file
View file

@ -0,0 +1,993 @@
/*
* Low-Level PCI Support for PC
*
* (c) 1999--2000 Martin Mares <mj@suse.cz>
*/
/* lots of mods by ron minnich (rminnich@lanl.gov), with
* the final architecture guidance from Tom Merritt (tjm@codegen.com)
* In particular, we changed from the one-pass original version to
* Tom's recommended multiple-pass version. I wasn't sure about doing
* it with multiple passes, until I actually started doing it and saw
* the wisdom of Tom's recommendations ...
*/
/* single-pass allocation appears to be the way to go. */
#include <lbpci.h>
#undef __KERNEL__
#include <asm/io.h>
#include <printk.h>
#ifdef EMULATE
#define DEBUG
#endif
#define DEBUG
#ifdef DEBUG
#define DBG(x...) printk(KERN_DEBUG x)
#else
#define DBG(x...)
#endif
#define ONEMEG (1 << 20)
#undef TWO_PASS_ALLOCATE
#define PCI_MEM_START 0x80000000
#define PCI_IO_START 0x1000
static const struct pci_ops *conf;
struct pci_ops {
int (*read_byte) (u8 bus, int devfn, int where, u8 * val);
int (*read_word) (u8 bus, int devfn, int where, u16 * val);
int (*read_dword) (u8 bus, int devfn, int where, u32 * val);
int (*write_byte) (u8 bus, int devfn, int where, u8 val);
int (*write_word) (u8 bus, int devfn, int where, u16 val);
int (*write_dword) (u8 bus, int devfn, int where, u32 val);
};
/*
* Direct access to PCI hardware...
*/
/*
* Functions for accessing PCI configuration space with type 1 accesses
*/
#define CONFIG_CMD(bus,devfn, where) (0x80000000 | (bus << 16) | (devfn << 8) | (where & ~3))
static int pci_conf1_read_config_byte(unsigned char bus, int devfn, int where, u8 * value)
{
outl(CONFIG_CMD(bus, devfn, where), 0xCF8);
*value = inb(0xCFC + (where & 3));
return 0;
}
static int pci_conf1_read_config_word(unsigned char bus, int devfn, int where, u16 * value)
{
outl(CONFIG_CMD(bus, devfn, where), 0xCF8);
*value = inw(0xCFC + (where & 2));
return 0;
}
static int pci_conf1_read_config_dword(unsigned char bus, int devfn, int where, u32 * value)
{
outl(CONFIG_CMD(bus, devfn, where), 0xCF8);
*value = inl(0xCFC);
return 0;
}
static int pci_conf1_write_config_byte(unsigned char bus, int devfn, int where, u8 value)
{
outl(CONFIG_CMD(bus, devfn, where), 0xCF8);
outb(value, 0xCFC + (where & 3));
return 0;
}
static int pci_conf1_write_config_word(unsigned char bus, int devfn, int where, u16 value)
{
outl(CONFIG_CMD(bus, devfn, where), 0xCF8);
outw(value, 0xCFC + (where & 2));
return 0;
}
static int pci_conf1_write_config_dword(unsigned char bus, int devfn, int where, u32 value)
{
outl(CONFIG_CMD(bus, devfn, where), 0xCF8);
outl(value, 0xCFC);
return 0;
}
#undef CONFIG_CMD
static const struct pci_ops pci_direct_conf1 =
{
pci_conf1_read_config_byte,
pci_conf1_read_config_word,
pci_conf1_read_config_dword,
pci_conf1_write_config_byte,
pci_conf1_write_config_word,
pci_conf1_write_config_dword
};
/*
* Functions for accessing PCI configuration space with type 2 accesses
*/
#define IOADDR(devfn, where) ((0xC000 | ((devfn & 0x78) << 5)) + where)
#define FUNC(devfn) (((devfn & 7) << 1) | 0xf0)
#define SET(bus,devfn) if (devfn & 0x80) return -1;outb(FUNC(devfn), 0xCF8); outb(bus, 0xCFA);
static int pci_conf2_read_config_byte(unsigned char bus, int devfn, int where, u8 * value)
{
SET(bus, devfn);
*value = inb(IOADDR(devfn, where));
outb(0, 0xCF8);
return 0;
}
static int pci_conf2_read_config_word(unsigned char bus, int devfn, int where, u16 * value)
{
SET(bus, devfn);
*value = inw(IOADDR(devfn, where));
outb(0, 0xCF8);
return 0;
}
static int pci_conf2_read_config_dword(unsigned char bus, int devfn, int where, u32 * value)
{
SET(bus, devfn);
*value = inl(IOADDR(devfn, where));
outb(0, 0xCF8);
return 0;
}
static int pci_conf2_write_config_byte(unsigned char bus, int devfn, int where, u8 value)
{
SET(bus, devfn);
outb(value, IOADDR(devfn, where));
outb(0, 0xCF8);
return 0;
}
static int pci_conf2_write_config_word(unsigned char bus, int devfn, int where, u16 value)
{
SET(bus, devfn);
outw(value, IOADDR(devfn, where));
outb(0, 0xCF8);
return 0;
}
static int pci_conf2_write_config_dword(unsigned char bus, int devfn, int where, u32 value)
{
SET(bus, devfn);
outl(value, IOADDR(devfn, where));
outb(0, 0xCF8);
return 0;
}
#undef SET
#undef IOADDR
#undef FUNC
static const struct pci_ops pci_direct_conf2 =
{
pci_conf2_read_config_byte,
pci_conf2_read_config_word,
pci_conf2_read_config_dword,
pci_conf2_write_config_byte,
pci_conf2_write_config_word,
pci_conf2_write_config_dword
};
/*
* Before we decide to use direct hardware access mechanisms, we try to do some
* trivial checks to ensure it at least _seems_ to be working -- we just test
* whether bus 00 contains a host bridge (this is similar to checking
* techniques used in XFree86, but ours should be more reliable since we
* attempt to make use of direct access hints provided by the PCI BIOS).
*
* This should be close to trivial, but it isn't, because there are buggy
* chipsets (yes, you guessed it, by Intel and Compaq) that have no class ID.
*/
static int pci_sanity_check(const struct pci_ops *o)
{
u16 x;
u8 bus;
int devfn;
#define PCI_CLASS_BRIDGE_HOST 0x0600
#define PCI_CLASS_DISPLAY_VGA 0x0300
#define PCI_VENDOR_ID_COMPAQ 0x0e11
#define PCI_VENDOR_ID_INTEL 0x8086
for (bus = 0, devfn = 0; devfn < 0x100; devfn++)
if ((!o->read_word(bus, devfn, PCI_CLASS_DEVICE, &x) &&
(x == PCI_CLASS_BRIDGE_HOST || x == PCI_CLASS_DISPLAY_VGA)) ||
(!o->read_word(bus, devfn, PCI_VENDOR_ID, &x) &&
(x == PCI_VENDOR_ID_INTEL || x == PCI_VENDOR_ID_COMPAQ)))
return 1;
printk(KERN_ERR "PCI: Sanity check failed\n");
return 0;
}
static const struct pci_ops *pci_check_direct(void)
{
unsigned int tmp;
/*
* Check if configuration type 1 works.
*/
{
outb(0x01, 0xCFB);
tmp = inl(0xCF8);
outl(0x80000000, 0xCF8);
if (inl(0xCF8) == 0x80000000 &&
pci_sanity_check(&pci_direct_conf1)) {
outl(tmp, 0xCF8);
printk(KERN_INFO "PCI: Using configuration type 1\n");
return &pci_direct_conf1;
}
outl(tmp, 0xCF8);
}
/*
* Check if configuration type 2 works.
*/
{
outb(0x00, 0xCFB);
outb(0x00, 0xCF8);
outb(0x00, 0xCFA);
if (inb(0xCF8) == 0x00 && inb(0xCFA) == 0x00 &&
pci_sanity_check(&pci_direct_conf2)) {
printk(KERN_INFO "PCI: Using configuration type 2\n");
return &pci_direct_conf2;
}
}
return 0;
}
// Need to merge all these functions. Sorry about this.
// changing horses in mid-stream!
int pci_read_config_byte(struct pci_dev *dev, u8 where, u8 * val)
{
return conf->read_byte(dev->bus->number, dev->devfn, where, val);
}
int pci_read_config_word(struct pci_dev *dev, u8 where, u16 * val)
{
return conf->read_word(dev->bus->number, dev->devfn, where, val);
}
int pci_read_config_dword(struct pci_dev *dev, u8 where, u32 * val)
{
return conf->read_dword(dev->bus->number, dev->devfn, where, val);
}
int pci_write_config_byte(struct pci_dev *dev, u8 where, u8 val)
{
return conf->write_byte(dev->bus->number, dev->devfn, where, val);
}
int pci_write_config_word(struct pci_dev *dev, u8 where, u16 val)
{
return conf->write_word(dev->bus->number, dev->devfn, where, val);
}
int pci_write_config_dword(struct pci_dev *dev, u8 where, u32 val)
{
return conf->write_dword(dev->bus->number, dev->devfn, where, val);
}
int pci_debugwrite_config_byte(struct pci_dev *dev, u8 where, u8 val)
{
#ifndef DEBUG
return conf->write_byte(dev->bus->number, dev->devfn, where, val);
#else
printk("Write config byte bus %d, devfn 0x%x, reg 0x%x, val 0x%x\n",
dev->bus->number, dev->devfn, where, val);
return 0;
#endif
}
int pci_debugwrite_config_word(struct pci_dev *dev, u8 where, u16 val)
{
#ifndef DEBUG
return conf->write_word(dev->bus->number, dev->devfn, where, val);
#else
printk("Write config byte bus %d, devfn 0x%x, reg 0x%x, val 0x%x\n",
dev->bus->number, dev->devfn, where, val);
return 0;
#endif
}
int pci_debugwrite_config_dword(struct pci_dev *dev, u8 where, u32 val)
{
#ifndef DEBUG
return conf->write_dword(dev->bus->number, dev->devfn, where, val);
#else
printk("Write config byte bus %d, devfn 0x%x, reg 0x%x, val 0x%x\n",
dev->bus->number, dev->devfn, where, val);
return 0;
#endif
}
int pcibios_read_config_byte(unsigned char bus, unsigned char devfn, u8 where, u8 * val)
{
return conf->read_byte(bus, devfn, where, val);
}
int pcibios_read_config_word(unsigned char bus, unsigned char devfn, u8 where, u16 * val)
{
return conf->read_word(bus, devfn, where, val);
}
int pcibios_read_config_dword(unsigned char bus, unsigned char devfn, u8 where, u32 * val)
{
return conf->read_dword(bus, devfn, where, val);
}
int pcibios_write_config_byte(unsigned char bus, unsigned char devfn, u8 where, u8 val)
{
return conf->write_byte(bus, devfn, where, val);
}
int pcibios_write_config_word(unsigned char bus, unsigned char devfn, u8 where, u16 val)
{
return conf->write_word(bus, devfn, where, val);
}
int pcibios_write_config_dword(unsigned char bus, unsigned char devfn, u8 where, u32 val)
{
return conf->write_dword(bus, devfn, where, val);
}
int pcibios_debugwrite_config_byte(unsigned char bus, unsigned char devfn, u8 where, u8 val)
{
#ifndef DEBUG
return conf->write_byte(bus, devfn, where, val);
#else
printk("Write byte bus %d, devfn 0x%x, reg 0x%x, val 0x%x\n",
bus, devfn, where, val);
return 0;
#endif
}
int pcibios_debugwrite_config_word(unsigned char bus, unsigned char devfn, u8 where, u16 val)
{
#ifndef DEBUG
return conf->write_word(bus, devfn, where, val);
#else
printk("Write word bus %d, devfn 0x%x, reg 0x%x, val 0x%x\n",
bus, devfn, where, val);
return 0;
#endif
}
int pcibios_debugwrite_config_dword(unsigned char bus, unsigned char devfn, u8 where, u32 val)
{
#ifndef DEBUG
return conf->write_dword(bus, devfn, where, val);
#else
printk("Write doubleword bus %d, devfn 0x%x, reg 0x%x, val 0x%x\n",
bus, devfn, where, val);
return 0;
#endif
}
/** round a number to an alignment.
* @param val the starting value
* @param roundup Alignment as a power of two
* @returns rounded up number
*/
unsigned long round(unsigned long val, unsigned long roundup)
{
// ROUNDUP MUST BE A POWER OF TWO.
unsigned long inverse;
inverse = ~(roundup - 1);
val += (roundup - 1);
val &= inverse;
return val;
}
/** Set the method to be used for PCI, type I or type II
*/
void pci_set_method()
{
conf = &pci_direct_conf1;
conf = pci_check_direct();
}
/* allocating resources on PCI is a mess. The reason is that
* the BAR size is actually two things: one is the size, and
* the other is the alignment of the data. Take, for example, the
* SiS agp hardware. BAR 0 reports a size as follows: 0xf8000008.
* This means prefetchable, and you can compute the size of
* 0x8000000 (128 Mbytes). But it also turns you that only the
* top five bits of the address are decoded. So you can not, for
* example, allocate address space at 0x400000 for 0x8000000 bytes,
* because in the register that will turn into 0. You have
* to allocate address space using only the top five bits of the
* PCI address space, i.e. you have to start allocating at 0x8000000.
*
* we have a more complex algorithm for address space allocation in the
* works, that is actually simple code but gets the desired behavior.
* For now, though, we operate as follows:
* as you encounter BAR values, just round up the current usage
* to be aligned to the BAR size. Then allocate.
* This has the advantage of being simple, and in practice there are
* so few large BAR areas that we expect it to cover all cases.
* If we find problems with this strategy we'll go to the more complex
* algorithm.
*/
/* it's worse than I thought ...
* rules:
* bridges contain all sub-bridges, and the address space for mem and
* prefetch has to be contiguous.
* Anyway, this has gotten so complicated we're going to a one-pass
* allocate for now.
*/
#ifdef TWO_PASS_ALLOCATE
void compute_resources(struct pci_bus *bus)
{
int i;
struct pci_bus *curbus;
struct pci_dev *curdev;
unsigned long mem, prefmem, io;
mem = prefmem = io = 0;
// first, walk all the bridges
// Sum up the memory requirements of each bridge and add it into the
// total.
for (curbus = bus->children; curbus; curbus = curbus->next) {
compute_resources(curbus);
mem += curbus->mem;
prefmem += curbus->prefmem;
io += curbus->io;
printk(KERN_DEBUG "Bridge Bus %d,mem 0x%lx, "
"prefmem 0x%lx, io 0x%lx\n",
curbus->number, mem, prefmem, io);
}
// second, walk all the devices. Add these.
for (curdev = bus->devices; curdev; curdev = curdev->sibling) {
for (i = 0; i < 6; i++) {
unsigned long size = curdev->size[i];
if (size & PCI_BASE_ADDRESS_SPACE_IO) {
unsigned long iosize;
iosize = size & PCI_BASE_ADDRESS_IO_MASK;
printk(KERN_DEBUG "Bus %d, devfn 0x%x, "
"reg %d: io 0x%lx\n",
curdev->bus->number, curdev->devfn,
i, iosize);
io += round(iosize, IO_ALIGN);
} else {
unsigned long memorysize = size & (PCI_BASE_ADDRESS_MEM_MASK);
unsigned long type = size & (~PCI_BASE_ADDRESS_MEM_MASK);
// at this point it's memory. What kind?
if (type == 0) { // normal
unsigned long regmem;
// align the memory value
regmem = round(memorysize, MEM_ALIGN);
mem = round(mem, regmem);
printk(KERN_DEBUG "Bus %d, "
"devfn 0x%x, reg %d: "
"mem 0x%lx\n",
curdev->bus->number,
curdev->devfn, i, regmem);
mem += regmem;
} else if (type & PCI_BASE_ADDRESS_MEM_PREFETCH) {
unsigned long regmem;
// align the memory value
regmem = round(memorysize, MEM_ALIGN);
prefmem = round(prefmem, regmem);
printk(KERN_DEBUG "Bus %d, "
"devfn 0x%x, reg %d: "
"prefmem 0x%lx\n",
curdev->bus->number,
curdev->devfn, i, regmem);
prefmem += regmem;
} else
printk(KERN_DEBUG "Bus %d, "
"devfn 0x%x: Unsupported "
"memory type 0x%lx\n",
curdev->bus->number,
curdev->devfn, type);
}
}
}
// assign to this bus.
bus->mem = round(mem, ONEMEG);
bus->prefmem = round(prefmem, ONEMEG);
bus->io = round(io, IO_BRIDGE_ALIGN);
}
void allocate_resources(struct pci_bus *bus)
{
int i;
struct pci_bus *curbus;
struct pci_dev *curdev;
unsigned long mem, io, prefmem;
// first, walk all the bridges
// Give each bridge what it needs, then call that bridge to configure
// itself and its devices.
mem = bus->membase;
prefmem = bus->prefmembase;
io = bus->iobase;
for (curbus = bus->children; curbus; curbus = curbus->next) {
unsigned long memincrement;
unsigned long ioincrement;
// we don't need to round here -- all sizes are already rounded!
if (curbus->mem) {
curbus->membase = mem;
memincrement = curbus->mem;
curbus->memlimit = curbus->membase + memincrement - 1;
mem += memincrement;
}
if (curbus->prefmem) {
curbus->prefmembase = prefmem;
memincrement = curbus->prefmem;
curbus->prefmemlimit = curbus->prefmembase + memincrement - 1;
prefmem += memincrement;
}
if (curbus->io) {
curbus->iobase = io;
ioincrement = curbus->io;
curbus->iolimit = curbus->io + ioincrement - 1;
io += ioincrement;
}
allocate_resources(curbus);
printk(KERN_DEBUG "Bridge Bus %d,mem 0x%lx, "
"prefmem 0x%lx, io 0x%lx\n",
curbus->number, mem, prefmem, io);
}
// Now hand out device memory.
for (curdev = bus->devices; curdev; curdev = curdev->sibling) {
for (i = 0; i < 6; i++) {
if (!curdev->size[i])
continue;
if (curdev->size[i] & PCI_BASE_ADDRESS_SPACE_IO) {
curdev->command |= PCI_COMMAND_IO;
curdev->base_address[i] = io;
printk(KERN_DEBUG "bus %d devfn 0x%x "
"reg %d io 0x%lx\n",
bus->number, curdev->devfn, i, io);
io += curdev->size[i] & PCI_BASE_ADDRESS_IO_MASK;
} else {
if (curdev->size[i] & PCI_BASE_ADDRESS_MEM_PREFETCH) {
unsigned long size = curdev->size[i] & PCI_BASE_ADDRESS_MEM_MASK;
// align the memory
size = round(size, MEM_ALIGN);
prefmem = round(prefmem, size);
curdev->command |= PCI_COMMAND_MEMORY;
curdev->base_address[i] = prefmem;
printk(KERN_DEBUG "bus %d devfn 0x%x "
"reg %d prefmem 0x%lx\n",
bus->number, curdev->devfn,
i, prefmem);
prefmem += size;
} else {
unsigned long size = curdev->size[i] & PCI_BASE_ADDRESS_MEM_MASK;
// align the memory
size = round(size, MEM_ALIGN);
mem = round(mem, size);
curdev->command |= PCI_COMMAND_MEMORY;
curdev->base_address[i] = mem;
printk(KERN_DEBUG "bus %d devfn 0x%x "
"reg %d mem 0x%lx\n",
bus->number, curdev->devfn,
i, mem);
mem += size;
}
}
}
}
}
#else /* single pass allocation */
/** Given a desired amount of io, round it to IO_BRIDGE_ALIGN
* @param amount Amount of memory desired.
*/
unsigned long iolimit(unsigned long amount)
{
amount = round(amount, IO_BRIDGE_ALIGN) - 1;
return amount;
}
/** Given a desired amount of memory, round it to ONEMEG
* @param amount Amount of memory desired.
*/
unsigned long memlimit(unsigned long amount)
{
amount = round(amount, ONEMEG) - 1;
return amount;
}
/** Compute and allocate the io for this bus.
* @param bus Pointer to the struct for this bus.
*/
void compute_allocate_io(struct pci_bus *bus)
{
int i;
struct pci_bus *curbus;
struct pci_dev *curdev;
unsigned long io_base;
io_base = bus->iobase;
DBG("compute_allocate_mem: base 0x%lx\n", bus->iobase);
/* First, walk all the bridges. When you return, grow the limit of the current bus
since sub-busses need IO rounded to 4096 */
for (curbus = bus->children; curbus; curbus = curbus->next) {
curbus->iobase = io_base;
compute_allocate_io(curbus);
io_base = round(curbus->iolimit, IO_BRIDGE_ALIGN);
DBG("BUSIO: done Bridge Bus 0x%x, iobase now 0x%lx\n",
curbus->number, io_base);
}
/* Walk through all the devices on current bus and oompute IO address space.*/
for (curdev = bus->devices; curdev; curdev = curdev->sibling) {
for (i = 0; i < 6; i++) {
unsigned long size = curdev->size[i];
if (size & PCI_BASE_ADDRESS_SPACE_IO) {
unsigned long iosize = size & PCI_BASE_ADDRESS_IO_MASK;
if (!iosize)
continue;
DBG("DEVIO: Bus 0x%x, devfn 0x%x, reg 0x%x: "
"iosize 0x%lx\n",
curdev->bus->number, curdev->devfn, i, iosize);
curdev->base_address[i] = io_base;
// some chipsets allow us to set/clear the IO bit.
// (e.g. VIA 82c686a.) So set it to be safe)
curdev->base_address[i] |=
PCI_BASE_ADDRESS_SPACE_IO;
DBG("-->set base to 0x%lx\n", io_base);
io_base += round(iosize, IO_ALIGN);
curdev->command |= PCI_COMMAND_IO;
}
}
}
bus->iolimit = iolimit(io_base);
DBG("BUS %d: set iolimit to 0x%lx\n", bus->number, bus->iolimit);
}
/** Compute and allocate the memory for this bus.
* @param bus Pointer to the struct for this bus.
*/
void compute_allocate_mem(struct pci_bus *bus)
{
int i;
struct pci_bus *curbus;
struct pci_dev *curdev;
unsigned long mem_base;
mem_base = bus->membase;
DBG("compute_allocate_mem: base 0x%lx\n", bus->membase);
/* First, walk all the bridges. When you return, grow the limit of the current bus
since sub-busses need MEMORY rounded to 1 Mega */
for (curbus = bus->children; curbus; curbus = curbus->next) {
curbus->membase = mem_base;
compute_allocate_mem(curbus);
mem_base = round(curbus->memlimit, ONEMEG);
DBG("BUSMEM: Bridge Bus 0x%x,membase now 0x%lx\n",
curbus->number, mem_base);
}
/* Walk through all the devices on current bus and oompute MEMORY address space.*/
for (curdev = bus->devices; curdev; curdev = curdev->sibling) {
for (i = 0; i < 6; i++) {
unsigned long size = curdev->size[i];
unsigned long memorysize = size & (PCI_BASE_ADDRESS_MEM_MASK);
unsigned long type = size & (~PCI_BASE_ADDRESS_MEM_MASK);
if (!memorysize)
continue;
if (type == 0) {
/* this is normal memory space */
unsigned long regmem;
/* PCI BUS Spec suggests that the memory address should be
consumed in 4KB unit */
regmem = round(memorysize, MEM_ALIGN);
mem_base = round(mem_base, regmem);
DBG("DEVMEM: Bus 0x%x, devfn 0x%x, reg 0x%x: "
"memsize 0x%lx\n",
curdev->bus->number, curdev->devfn, i, regmem);
curdev->base_address[i] = mem_base;
DBG("-->set base to 0x%lx\n", mem_base);
mem_base += regmem;
curdev->command |= PCI_COMMAND_MEMORY;
}
}
}
bus->memlimit = memlimit(mem_base);
DBG("BUS %d: set memlimit to 0x%lx\n", bus->number, bus->memlimit);
}
/** Compute and allocate the prefetch memory for this bus.
* @param bus Pointer to the struct for this bus.
*/
void compute_allocate_prefmem(struct pci_bus *bus)
{
int i;
struct pci_bus *curbus;
struct pci_dev *curdev;
unsigned long prefmem_base;
prefmem_base = bus->prefmembase;
DBG("Compute_allocate_prefmem: base 0x%lx\n", bus->prefmembase);
/* First, walk all the bridges. When you return, grow the limit of the current bus
since sub-busses need MEMORY rounded to 1 Mega */
for (curbus = bus->children; curbus; curbus = curbus->next) {
curbus->prefmembase = prefmem_base;
compute_allocate_prefmem(curbus);
prefmem_base = round(curbus->prefmemlimit, ONEMEG);
DBG("BUSPREFMEM: Bridge Bus 0x%x, prefmem base now 0x%lx\n",
curbus->number, prefmem_base);
}
/* Walk through all the devices on current bus and oompute PREFETCHABLE MEMORY address space.*/
for (curdev = bus->devices; curdev; curdev = curdev->sibling) {
for (i = 0; i < 6; i++) {
unsigned long size = curdev->size[i];
unsigned long memorysize = size & (PCI_BASE_ADDRESS_MEM_MASK);
unsigned long type = size & (~PCI_BASE_ADDRESS_MEM_MASK);
if (!memorysize)
continue;
if (type & PCI_BASE_ADDRESS_MEM_PREFETCH) {
/* this is a prefetchable memory area */
unsigned long regmem;
/* PCI BUS Spec suggests that the memory address should be
consumed in 4KB unit */
regmem = round(memorysize, MEM_ALIGN);
prefmem_base = round(prefmem_base, regmem);
DBG("DEVPREFMEM: Bus 0x%x, devfn 0x%x, reg 0x%x: "
"prefmemsize 0x%lx\n",
curdev->bus->number, curdev->devfn, i, regmem);
curdev->base_address[i] = prefmem_base;
DBG("-->set base to 0x%lx\n", prefmem_base);
prefmem_base += regmem;
curdev->command |= PCI_COMMAND_MEMORY;
}
}
}
bus->prefmemlimit = memlimit(prefmem_base);
DBG("BUS %d: set prefmemlimit to 0x%lx\n", bus->number, bus->prefmemlimit);
}
/** Compute and allocate resources.
* This is a one-pass process. We first compute all the IO, then
* memory, then prefetchable memory.
* This is really only called at the top level
* @param bus Pointer to the struct for this bus.
*/
void compute_allocate_resources(struct pci_bus *bus)
{
DBG("COMPUTE_ALLOCATE: do IO\n");
compute_allocate_io(bus);
DBG("COMPUTE_ALLOCATE: do MEM\n");
compute_allocate_mem(bus);
// now put the prefetchable memory at the end of the memory
bus->prefmembase = round(bus->memlimit, ONEMEG);
DBG("COMPUTE_ALLOCATE: do PREFMEM\n");
compute_allocate_prefmem(bus);
}
#endif /* TWO_PASS_ALLOCATE */
/** Assign the computed resources to the bridges and devices on the bus.
* Recurse to any bridges found on this bus first. Then do the devices
* on this bus.
* @param bus Pointer to the structure for this bus
*/
void assign_resources(struct pci_bus *bus)
{
struct pci_dev *curdev = pci_devices;
struct pci_bus *curbus;
DBG("ASSIGN RESOURCES, bus %d\n", bus->number);
/* wlak trhough all the buses, assign resources for bridges */
for (curbus = bus->children; curbus; curbus = curbus->next) {
curbus->self->command = 0;
/* set the IO ranges
WARNING: we don't really do 32-bit addressing for IO yet! */
if (curbus->iobase) {
curbus->self->command |= PCI_COMMAND_IO;
pci_write_config_byte(curbus->self, PCI_IO_BASE,
curbus->iobase >> 8);
pci_write_config_byte(curbus->self, PCI_IO_LIMIT,
curbus->iolimit >> 8);
DBG("Bus 0x%x iobase to 0x%x iolimit 0x%x\n",
bus->number, curbus->iobase, curbus->iolimit);
}
// set the memory range
if (curbus->membase) {
curbus->self->command |= PCI_COMMAND_MEMORY;
pci_write_config_word(curbus->self, PCI_MEMORY_BASE,
curbus->membase >> 16);
pci_write_config_word(curbus->self, PCI_MEMORY_LIMIT,
curbus->memlimit >> 16);
DBG("Bus 0x%x membase to 0x%x memlimit 0x%x\n",
bus->number, curbus->membase, curbus->memlimit);
}
// set the prefetchable memory range
if (curbus->prefmembase) {
curbus->self->command |= PCI_COMMAND_MEMORY;
pci_write_config_word(curbus->self, PCI_PREF_MEMORY_BASE,
curbus->prefmembase >> 16);
pci_write_config_word(curbus->self, PCI_PREF_MEMORY_LIMIT,
curbus->prefmemlimit >> 16);
DBG("Bus 0x%x prefmembase to 0x%x prefmemlimit 0x%x\n",
bus->number, curbus->prefmembase, curbus->prefmemlimit);
}
curbus->self->command |= PCI_COMMAND_MASTER;
}
for (curdev = pci_devices; curdev; curdev = curdev->next) {
int i;
for (i = 0; i < 6; i++) {
unsigned long reg;
if (curdev->base_address[i] == 0)
continue;
reg = PCI_BASE_ADDRESS_0 + (i << 2);
pci_write_config_dword(curdev, reg, curdev->base_address[i]);
DBG("Bus 0x%x devfn 0x%x reg 0x%x base to 0x%lx\n",
curdev->bus->number, curdev->devfn, i,
curdev->base_address[i]);
}
}
}
void enable_resources(struct pci_bus *bus)
{
struct pci_dev *curdev = pci_devices;
/* walk through the chain of all pci device, this time we don't have to deal
with the device v.s. bridge stuff, since every bridge has its own pci_dev
assocaited with it */
for (curdev = pci_devices; curdev; curdev = curdev->next) {
u16 command;
pci_read_config_word(curdev, PCI_COMMAND, &command);
command |= curdev->command;
pci_write_config_word(curdev, PCI_COMMAND, command);
DBG("DEV Set command bus 0x%x devfn 0x%x to 0x%x\n",
curdev->bus->number, curdev->devfn, command);
}
}
/** Enumerate the resources on the PCI by calling pci_init
*/
void pci_enumerate()
{
// scan it.
pci_init();
}
/** Starting at the root, compute what resources are needed and allocate them.
* We start memory, prefetchable memory at PCI_MEM_START. I/O starts at
* PCI_IO_START. Since the assignment is hierarchical we set the values
* into the pci_root struct.
*/
void pci_configure()
{
pci_root.membase = PCI_MEM_START;
pci_root.prefmembase = PCI_MEM_START;
pci_root.iobase = PCI_IO_START;
compute_allocate_resources(&pci_root);
// now just set things into registers ... we hope ...
assign_resources(&pci_root);
}
/** Starting at the root, walk the tree and enable all devices/bridges.
* What really happens is computed COMMAND bits get set in register 4
*/
void pci_enable()
{
// now enable everything.
enable_resources(&pci_root);
}
// is this dead code? think so. -- RGM
int configure_pci(unsigned long memstart, unsigned long iostart)
{
#ifdef EMULATE
int iopl(int level);
iopl(3);
// pick how to scan the bus
pci_set_method();
#else
// FIX ME. We really should use whatever is set up elsewhere.
void malloc_init(unsigned long start, unsigned long end);
// pick the last 1M of memory to put our structures in.
malloc_init(memstart - ONEMEG, memstart - 1);
#endif
// scan it.
pci_init();
#ifdef TWO_PASS_ALLOCATE
/* compute the memory resources. You can't just add up the
* per-device resources. You have to walk the bridges, because
* bridges require 1M granularity at 1M alignments (!)
*/
pci_root.mem = pci_root.prefmem = pci_root.io = 0;
compute_resources(&pci_root);
printk(KERN_DEBUG "Total: 0x%lx mem, 0x%lx prefmem, 0x%lx io\n",
pci_root.mem, pci_root.prefmem, pci_root.io);
// set in the bases and limits for the root. These really apply to the
// subordinate busses and devices.
pci_root.membase = memstart;
pci_root.memlimit = pci_root.membase + pci_root.mem - 1;
pci_root.prefmembase = round(pci_root.memlimit, ONEMEG);
pci_root.prefmemlimit = pci_root.prefmembase + pci_root.prefmem - 1;
pci_root.iobase = iostart;
pci_root.iolimit = 0xffff - iostart;
allocate_resources(&pci_root);
#else
pci_root.membase = PCI_MEM_START;
pci_root.prefmembase = PCI_MEM_START;
pci_root.iobase = PCI_IO_START;
compute_allocate_resources(&pci_root);
#endif /* TWO_PASS_ALLOCATE */
// now just set things into registers ... we hope ...
assign_resources(&pci_root);
// now enable everything.
enable_resources(&pci_root);
return 0;
}
#ifdef EMULATE
/** deprecated, you can run this code in user mode for certain testing
* We haven't used this recently so it's unclear if it works.
*/
int main()
{
unsigned long memstart = 0x40000000;
unsigned long iostart = 0x1000;
configure_pci(memstart, iostart);
return 0;
}
#endif

62
src/lib/printk.c Normal file
View file

@ -0,0 +1,62 @@
/*
* blantantly copied from linux/kernel/printk.c
*
* Copyright (C) 1991, 1992 Linus Torvalds
*
*/
//typedef void * va_list;
#include <stdarg.h>
static char buf[1024];
/* printk's without a loglevel use this.. */
#define DEFAULT_MESSAGE_LOGLEVEL 4 /* KERN_WARNING */
/* We show everything that is MORE important than this.. */
#define MINIMUM_CONSOLE_LOGLEVEL 1 /* Minimum loglevel we let people use */
#ifndef DEFAULT_CONSOLE_LOGLEVEL
#define DEFAULT_CONSOLE_LOGLEVEL 8 /* anything MORE serious than KERN_WARNING */
#endif
/* Keep together for sysctl support */
int console_loglevel = DEFAULT_CONSOLE_LOGLEVEL;
int default_message_loglevel = DEFAULT_MESSAGE_LOGLEVEL;
int minimum_console_loglevel = MINIMUM_CONSOLE_LOGLEVEL;
int default_console_loglevel = DEFAULT_CONSOLE_LOGLEVEL;
void display(char*);
extern int vsprintf(char *buf, const char *, va_list);
int printk(const char *fmt, ...)
{
va_list args;
int i;
char *p;
int msg_level;
va_start(args, fmt);
i = vsprintf(buf, fmt, args); /* hopefully i < sizeof(buf)-4 */
va_end(args);
p = buf;
if (
p[0] == '<' &&
p[1] > '0' &&
p[1] <= '9' &&
p[2] == '>'
) {
msg_level = p[1] - '0';
p +=3;
} else {
msg_level = default_message_loglevel;
}
if (msg_level < console_loglevel) {
display(p);
}
return i;
}

331
src/lib/vsprintf.c Normal file
View file

@ -0,0 +1,331 @@
/*
* linux/lib/vsprintf.c
*
* Copyright (C) 1991, 1992 Linus Torvalds
*/
/* vsprintf.c -- Lars Wirzenius & Linus Torvalds. */
/*
* Wirzenius wrote this portably, Torvalds fucked it up :-)
*/
#include <stdarg.h>
#include <linux/types.h>
#include <linux/string.h>
/* haha, don't need ctype.c */
#define isdigit(c) ((c) >= '0' && (c) <= '9')
#define is_digit isdigit
#define isxdigit(c) (((c) >= '0' && (c) <= '9') || ((c) >= 'a' && (c) <= 'f') || ((c) >= 'A' && (c) <= 'F'))
#define islower(c) ((c) >= 'a' && (c) <= 'z')
#define toupper(c) __toupper(c)
static inline unsigned char __toupper(unsigned char c)
{
if (islower(c))
c -= 'a'-'A';
return c;
}
unsigned long simple_strtoul(const char *cp,char **endp,unsigned int base)
{
unsigned long result = 0,value;
if (!base) {
base = 10;
if (*cp == '0') {
base = 8;
cp++;
if ((*cp == 'x') && isxdigit(cp[1])) {
cp++;
base = 16;
}
}
}
while (isxdigit(*cp) && (value = isdigit(*cp) ? *cp-'0' : (islower(*cp)
? toupper(*cp) : *cp)-'A'+10) < base) {
result = result*base + value;
cp++;
}
if (endp)
*endp = (char *)cp;
return result;
}
long simple_strtol(const char *cp,char **endp,unsigned int base)
{
if(*cp=='-')
return -simple_strtoul(cp+1,endp,base);
return simple_strtoul(cp,endp,base);
}
static int skip_atoi(const char **s)
{
int i=0;
while (is_digit(**s))
i = i*10 + *((*s)++) - '0';
return i;
}
#define ZEROPAD 1 /* pad with zero */
#define SIGN 2 /* unsigned/signed long */
#define PLUS 4 /* show plus */
#define SPACE 8 /* space if plus */
#define LEFT 16 /* left justified */
#define SPECIAL 32 /* 0x */
#define LARGE 64 /* use 'ABCDEF' instead of 'abcdef' */
#define do_div(n,base) ({ \
int __res; \
__res = ((unsigned long) n) % (unsigned) base; \
n = ((unsigned long) n) / (unsigned) base; \
__res; })
static char * number(char * str, long num, int base, int size, int precision
,int type)
{
char c,sign,tmp[66];
const char *digits="0123456789abcdefghijklmnopqrstuvwxyz";
int i;
if (type & LARGE)
digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
if (type & LEFT)
type &= ~ZEROPAD;
if (base < 2 || base > 36)
return 0;
c = (type & ZEROPAD) ? '0' : ' ';
sign = 0;
if (type & SIGN) {
if (num < 0) {
sign = '-';
num = -num;
size--;
} else if (type & PLUS) {
sign = '+';
size--;
} else if (type & SPACE) {
sign = ' ';
size--;
}
}
if (type & SPECIAL) {
if (base == 16)
size -= 2;
else if (base == 8)
size--;
}
i = 0;
if (num == 0)
tmp[i++]='0';
else while (num != 0)
tmp[i++] = digits[do_div(num,base)];
if (i > precision)
precision = i;
size -= precision;
if (!(type&(ZEROPAD+LEFT)))
while(size-->0)
*str++ = ' ';
if (sign)
*str++ = sign;
if (type & SPECIAL) {
if (base==8)
*str++ = '0';
else if (base==16) {
*str++ = '0';
*str++ = digits[33];
}
}
if (!(type & LEFT))
while (size-- > 0)
*str++ = c;
while (i < precision--)
*str++ = '0';
while (i-- > 0)
*str++ = tmp[i];
while (size-- > 0)
*str++ = ' ';
return str;
}
/* Forward decl. needed for IP address printing stuff... */
int sprintf(char * buf, const char *fmt, ...);
int vsprintf(char *buf, const char *fmt, va_list args)
{
int len;
unsigned long num;
int i, base;
char * str;
const char *s;
int flags; /* flags to number() */
int field_width; /* width of output field */
int precision; /* min. # of digits for integers; max
number of chars for from string */
int qualifier; /* 'h', 'l', or 'L' for integer fields */
for (str=buf ; *fmt ; ++fmt) {
if (*fmt != '%') {
*str++ = *fmt;
continue;
}
/* process flags */
flags = 0;
repeat:
++fmt; /* this also skips first '%' */
switch (*fmt) {
case '-': flags |= LEFT; goto repeat;
case '+': flags |= PLUS; goto repeat;
case ' ': flags |= SPACE; goto repeat;
case '#': flags |= SPECIAL; goto repeat;
case '0': flags |= ZEROPAD; goto repeat;
}
/* get field width */
field_width = -1;
if (is_digit(*fmt))
field_width = skip_atoi(&fmt);
else if (*fmt == '*') {
++fmt;
/* it's the next argument */
field_width = va_arg(args, int);
if (field_width < 0) {
field_width = -field_width;
flags |= LEFT;
}
}
/* get the precision */
precision = -1;
if (*fmt == '.') {
++fmt;
if (is_digit(*fmt))
precision = skip_atoi(&fmt);
else if (*fmt == '*') {
++fmt;
/* it's the next argument */
precision = va_arg(args, int);
}
if (precision < 0)
precision = 0;
}
/* get the conversion qualifier */
qualifier = -1;
if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L') {
qualifier = *fmt;
++fmt;
}
/* default base */
base = 10;
switch (*fmt) {
case 'c':
if (!(flags & LEFT))
while (--field_width > 0)
*str++ = ' ';
*str++ = (unsigned char) va_arg(args, int);
while (--field_width > 0)
*str++ = ' ';
continue;
case 's':
s = va_arg(args, char *);
if (!s)
s = "<NULL>";
len = strnlen(s, precision);
if (!(flags & LEFT))
while (len < field_width--)
*str++ = ' ';
for (i = 0; i < len; ++i)
*str++ = *s++;
while (len < field_width--)
*str++ = ' ';
continue;
case 'p':
if (field_width == -1) {
field_width = 2*sizeof(void *);
flags |= ZEROPAD;
}
str = number(str,
(unsigned long) va_arg(args, void *), 16,
field_width, precision, flags);
continue;
case 'n':
if (qualifier == 'l') {
long * ip = va_arg(args, long *);
*ip = (str - buf);
} else {
int * ip = va_arg(args, int *);
*ip = (str - buf);
}
continue;
case '%':
*str++ = '%';
continue;
/* integer number formats - set up the flags and "break" */
case 'o':
base = 8;
break;
case 'X':
flags |= LARGE;
case 'x':
base = 16;
break;
case 'd':
case 'i':
flags |= SIGN;
case 'u':
break;
default:
*str++ = '%';
if (*fmt)
*str++ = *fmt;
else
--fmt;
continue;
}
if (qualifier == 'l')
num = va_arg(args, unsigned long);
else if (qualifier == 'h') {
num = (unsigned short) va_arg(args, int);
if (flags & SIGN)
num = (short) num;
} else if (flags & SIGN)
num = va_arg(args, int);
else
num = va_arg(args, unsigned int);
str = number(str, num, base, field_width, precision, flags);
}
*str = '\0';
return str-buf;
}
int sprintf(char * buf, const char *fmt, ...)
{
va_list args;
int i;
va_start(args, fmt);
i=vsprintf(buf,fmt,args);
va_end(args);
return i;
}

192
src/pc80/serial.inc Normal file
View file

@ -0,0 +1,192 @@
/* Base Address */
#define TTYS0 0x3f8
/* Data */
#define TTYS0_RBR (TTYS0+0x00)
/* Control */
#define TTYS0_TBR TTYS0_RBR
#define TTYS0_IER (TTYS0+0x01)
#define TTYS0_IIR (TTYS0+0x02)
#define TTYS0_FCR TTYS0_IIR
#define TTYS0_LCR (TTYS0+0x03)
#define TTYS0_MCR (TTYS0+0x04)
#define TTYS0_DLL TTYS0_RBR
#define TTYS0_DLM TTYS0_IER
/* Status */
#define TTYS0_LSR (TTYS0+0x05)
#define TTYS0_MSR (TTYS0+0x06)
#define TTYS0_SCR (TTYS0+0x07)
jmp serial0
/* uses: ax, dx */
#define TTYS0_TX_AL \
mov %al, %ah ; \
9: mov $TTYS0_LSR, %dx ; \
inb %dx, %al ; \
test $0x20, %al ; \
je 9b ; \
mov $TTYS0_TBR, %dx ; \
mov %ah, %al ; \
outb %al, %dx
/* uses: esp, ax, dx */
#define TTYS0_TX_CHAR(byte) \
mov byte, %al ; \
CALLSP(ttys0_tx_al)
/* uses: esp, ax, dx */
#define TTYS0_TX_HEX8(byte) \
mov byte, %al ; \
CALLSP(ttys0_tx_hex8)
/* uses: esp, eax, ebx, dx */
#define TTYS0_TX_HEX32(lword) \
mov lword, %eax ; \
CALLSP(ttys0_tx_hex32)
/* uses: esp, ebx, ax, dx */
#define TTYS0_TX_STRING(string) \
mov string, %ebx ; \
CALLSP(ttys0_tx_string)
/* uses: esp, ax, dx */
ttys0_tx_al:
TTYS0_TX_AL
RETSP
/* uses: esp, ax, dx */
ttys0_tx_hex8:
mov $TTYS0_SCR, %dx
outb %al, %dx
shr $4, %al
add $'0', %al
cmp $'9', %al
jle 9f
add $39, %al
9:
TTYS0_TX_AL
mov $TTYS0_SCR, %dx
inb %dx, %al
and $0x0f, %al
add $'0', %al
cmp $'9', %al
jle 9f
add $39, %al
9:
TTYS0_TX_AL
RETSP
/* uses: esp, ebx, eax, dx */
ttys0_tx_hex32:
mov %eax, %ebx
shr $28, %eax
add $'0', %al
cmp $'9', %al
jle 9f
add $39, %al
9:
TTYS0_TX_AL
mov %ebx, %eax
shr $24, %eax
and $0x0f, %al
add $'0', %al
cmp $'9', %al
jle 9f
add $39, %al
9:
TTYS0_TX_AL
mov %ebx, %eax
shr $20, %eax
and $0x0f, %al
add $'0', %al
cmp $'9', %al
jle 9f
add $39, %al
9:
TTYS0_TX_AL
mov %ebx, %eax
shr $16, %eax
and $0x0f, %al
add $'0', %al
cmp $'9', %al
jle 9f
add $39, %al
9:
TTYS0_TX_AL
mov %ebx, %eax
shr $12, %eax
and $0x0f, %al
add $'0', %al
cmp $'9', %al
jle 9f
add $39, %al
9:
TTYS0_TX_AL
mov %ebx, %eax
shr $8, %eax
and $0x0f, %al
add $'0', %al
cmp $'9', %al
jle 9f
add $39, %al
9:
TTYS0_TX_AL
mov %ebx, %eax
shr $4, %eax
and $0x0f, %al
add $'0', %al
cmp $'9', %al
jle 9f
add $39, %al
9:
TTYS0_TX_AL
mov %ebx, %eax
and $0x0f, %al
add $'0', %al
cmp $'9', %al
jle 9f
add $39, %al
9:
TTYS0_TX_AL
RETSP
/* Uses esp, ebx, ax, dx */
ttys0_tx_string:
mov (%ebx), %al
inc %ebx
cmp $0, %al
jne 9f
RETSP
9: TTYS0_TX_AL
jmp ttys0_tx_string
serial0:
/* Set 115.2Kbps,8n1 */
mov $TTYS0_LCR, %dx
mov $0x83, %al
out %al, %dx
mov $TTYS0_DLL, %dx
mov $0x01, %al
out %al, %dx
mov $TTYS0_DLM, %dx
mov $0x00, %al
out %al, %dx
mov $TTYS0_LCR, %dx
mov $0x03, %al
out %al, %dx