Files
lk/dev/bus/pci/backend/bios32.cpp
Travis Geiselbrecht 664bb17afa [ubsan] fix some bugs and warnings discovered by ubsan
- X86 cpuid feature list dump was using the wrong array and walking off
  the end of one.
- GICv2 code had a left shift by up to 31 of an integer. Needs to be
  unsigned.
- PLIC same as GIC code.
- fdtwalker code should be using a bytewise accessor based helper
  function for reading large integers out of an unaliged FDT.
- PCI BIOS32 search code could do a 32bit unaligned read of a string,
  switch to using memcmp.
2025-10-05 15:35:31 -07:00

328 lines
8.2 KiB
C++

/*
* Copyright (c) 2009 Corey Tabaka
* Copyright (c) 2020 Travis Geiselbrecht
*
* Use of this source code is governed by a MIT-style
* license that can be found in the LICENSE file or at
* https://opensource.org/licenses/MIT
*/
#include "bios32.h"
#include <lk/debug.h>
#include <lk/err.h>
#include <stdlib.h>
#include <string.h>
#include <kernel/thread.h>
#include <kernel/spinlock.h>
#include <dev/bus/pci.h>
#include <lk/trace.h>
#include "../pci_priv.h"
#if ARCH_X86_32
// Only actually supported on x86-32
#include <arch/x86/descriptor.h>
#define LOCAL_TRACE 0
/*
* PCI BIOS access return codes
*/
// TODO: write routine to convert these to LK errors
#define _PCI_SUCCESSFUL 0x00
#define _PCI_FUNC_NOT_SUPPORTED 0x81
#define _PCI_BAD_VENDOR_ID 0x83
#define _PCI_DEVICE_NOT_FOUND 0x86
#define _PCI_BAD_REGISTER_NUMBER 0x87
#define _PCI_SET_FAILED 0x88
#define _PCI_BUFFER_TOO_SMALL 0x89
#define PCIBIOS_PRESENT 0xB101
#define PCIBIOS_FIND_PCI_DEVICE 0xB102
#define PCIBIOS_FIND_PCI_CLASS_CODE 0xB103
#define PCIBIOS_GENERATE_SPECIAL_CYCLE 0xB106
#define PCIBIOS_READ_CONFIG_BYTE 0xB108
#define PCIBIOS_READ_CONFIG_WORD 0xB109
#define PCIBIOS_READ_CONFIG_DWORD 0xB10A
#define PCIBIOS_WRITE_CONFIG_BYTE 0xB10B
#define PCIBIOS_WRITE_CONFIG_WORD 0xB10C
#define PCIBIOS_WRITE_CONFIG_DWORD 0xB10D
#define PCIBIOS_GET_IRQ_ROUTING_OPTIONS 0xB10E
#define PCIBIOS_PCI_SET_IRQ_HW_INT 0xB10F
#define PCIBIOS_SUCCESSFUL 0x00
#define PCIBIOS_FUNC_NOT_SUPPORTED 0x81
#define PCIBIOS_BAD_VENDOR_ID 0x83
#define PCIBIOS_DEVICE_NOT_FOUND 0x86
#define PCIBIOS_BAD_REGISTER_NUMBER 0x87
#define PCIBIOS_SET_FAILED 0x88
#define PCIBIOS_BUFFER_TOO_SMALL 0x89
/*
* BIOS32 entry header
*/
typedef struct {
uint8_t magic[4]; // "_32_"
void *entry; // entry point
uint8_t revision;
uint8_t length;
uint8_t checksum;
uint8_t reserved[5];
} __PACKED pci_bios_info;
/*
* scan for pci bios
*/
static const char *pci_bios_magic = "_32_";
static pci_bios_info *find_pci_bios_info(void) {
uint32_t *head = (uint32_t *) (0x000e0000 + KERNEL_BASE);
int8_t sum, *b;
uint i;
while (head < (uint32_t *) (0x000ffff0 + KERNEL_BASE)) {
if (memcmp(head, pci_bios_magic, sizeof(pci_bios_magic)) == 0) {
// perform the checksum
sum = 0;
b = (int8_t *) head;
for (i=0; i < sizeof(pci_bios_info); i++) {
sum += b[i];
}
if (sum == 0) {
return (pci_bios_info *) head;
}
}
head += 4;
}
return NULL;
}
/*
* local BIOS32 PCI routines
*/
static const char *pci_signature = "PCI ";
// new C++ version
pci_bios32 *pci_bios32::detect() {
LTRACE_ENTRY;
pci_bios_info *pci = find_pci_bios_info();
if (!pci) {
return nullptr;
}
dprintf(INFO, "PCI: found BIOS32 structure at %p\n", pci);
LTRACEF("BIOS32 header info:\n");
LTRACEF("magic '%c%c%c%c' entry %p len %d checksum %#hhx\n",
pci->magic[0], pci->magic[1], pci->magic[2], pci->magic[3],
pci->entry, pci->length * 16, pci->checksum);
uint32_t adr, temp, len;
uint8_t err;
bios32_entry b32_entry;
b32_entry.offset = (uint32_t)(uintptr_t)pci->entry + KERNEL_BASE;
b32_entry.selector = CODE_SELECTOR;
__asm__(
"lcall *(%%edi)"
: "=a"(err), /* AL out=status */
"=b"(adr), /* EBX out=code segment base adr */
"=c"(len), /* ECX out=code segment size */
"=d"(temp) /* EDX out=entry pt offset in code */
: "0"(0x49435024),/* EAX in=service="$PCI" */
"1"(0), /* EBX in=0=get service entry pt */
"D"(&b32_entry)
: "cc", "memory"
);
if (err == 0x80) {
dprintf(INFO, "BIOS32 found, but no PCI BIOS\n");
return nullptr;
}
if (err != 0) {
dprintf(INFO, "BIOS32 call to locate PCI BIOS returned %x\n", err);
return nullptr;
}
LTRACEF("BIOS32 entry segment base %#x offset %#x\n", adr, temp);
b32_entry.offset = adr + temp + KERNEL_BASE;
// now call PCI_BIOS_PRESENT to get version, hw mechanism, and last bus
uint16_t present, version, busses;
uint32_t signature;
__asm__(
"lcall *(%%edi) \n\t"
"jc 1f \n\t"
"xor %%ah,%%ah \n"
"1:"
: "=a"(present),
"=b"(version),
"=c"(busses),
"=d"(signature)
: "0"(PCIBIOS_PRESENT),
"D"(&b32_entry)
: "cc", "memory"
);
LTRACEF("PCI_BIOS_PRESENT returns present %#x, version %#x, busses %#x, signature %#x\n",
present, version, busses, signature);
if (present & 0xff00) {
dprintf(INFO, "PCI_BIOS_PRESENT call returned ah=%#02x\n", present >> 8);
return nullptr;
}
if (signature != *(uint32_t *)pci_signature) {
dprintf(INFO, "PCI_BIOS_PRESENT call returned edx=%#08x\n", signature);
return nullptr;
}
int last_bus = busses & 0xff;
auto b32 = new pci_bios32(b32_entry);
b32->set_last_bus(last_bus);
return b32;
}
int pci_bios32::read_config_byte(const pci_location_t state, uint32_t reg, uint8_t *value) {
uint32_t bx, ret;
bx = state.bus;
bx <<= 8;
bx |= (state.dev << 3) | state.fn;
__asm__(
"lcall *(%%esi) \n\t"
"jc 1f \n\t"
"xor %%ah,%%ah \n"
"1:"
: "=c"(*value),
"=a"(ret)
: "1"(PCIBIOS_READ_CONFIG_BYTE),
"b"(bx),
"D"(reg),
"S"(&bios32_entry_)
: "cc", "memory");
ret >>= 8;
return ret & 0xFF;
}
int pci_bios32::read_config_half(const pci_location_t state, uint32_t reg, uint16_t *value) {
uint32_t bx, ret;
bx = state.bus;
bx <<= 8;
bx |= (state.dev << 3) | state.fn;
__asm__(
"lcall *(%%esi) \n\t"
"jc 1f \n\t"
"xor %%ah,%%ah \n"
"1:"
: "=c"(*value),
"=a"(ret)
: "1"(PCIBIOS_READ_CONFIG_WORD),
"b"(bx),
"D"(reg),
"S"(&bios32_entry_)
: "cc", "memory");
ret >>= 8;
return ret & 0xFF;
}
int pci_bios32::read_config_word(const pci_location_t state, uint32_t reg, uint32_t *value) {
uint32_t bx, ret;
bx = state.bus;
bx <<= 8;
bx |= (state.dev << 3) | state.fn;
__asm__(
"lcall *(%%esi) \n\t"
"jc 1f \n\t"
"xor %%ah,%%ah \n"
"1:"
: "=c"(*value),
"=a"(ret)
: "1"(PCIBIOS_READ_CONFIG_DWORD),
"b"(bx),
"D"(reg),
"S"(&bios32_entry_)
: "cc", "memory");
ret >>= 8;
return ret & 0xFF;
}
int pci_bios32::write_config_byte(const pci_location_t state, uint32_t reg, uint8_t value) {
uint32_t bx, ret;
bx = state.bus;
bx <<= 8;
bx |= (state.dev << 3) | state.fn;
__asm__(
"lcall *(%%esi) \n\t"
"jc 1f \n\t"
"xor %%ah,%%ah \n"
"1:"
: "=a"(ret)
: "0"(PCIBIOS_WRITE_CONFIG_BYTE),
"c"(value),
"b"(bx),
"D"(reg),
"S"(&bios32_entry_)
: "cc", "memory");
ret >>= 8;
return ret & 0xFF;
}
int pci_bios32::write_config_half(const pci_location_t state, uint32_t reg, uint16_t value) {
uint32_t bx, ret;
bx = state.bus;
bx <<= 8;
bx |= (state.dev << 3) | state.fn;
__asm__(
"lcall *(%%esi) \n\t"
"jc 1f \n\t"
"xor %%ah,%%ah \n"
"1:"
: "=a"(ret)
: "0"(PCIBIOS_WRITE_CONFIG_WORD),
"c"(value),
"b"(bx),
"D"(reg),
"S"(&bios32_entry_)
: "cc", "memory");
ret >>= 8;
return ret & 0xFF;
}
int pci_bios32::write_config_word(const pci_location_t state, uint32_t reg, uint32_t value) {
uint32_t bx, ret;
bx = state.bus;
bx <<= 8;
bx |= (state.dev << 3) | state.fn;
__asm__(
"lcall *(%%esi) \n\t"
"jc 1f \n\t"
"xor %%ah,%%ah \n"
"1:"
: "=a"(ret)
: "0"(PCIBIOS_WRITE_CONFIG_DWORD),
"c"(value),
"b"(bx),
"D"(reg),
"S"(&bios32_entry_)
: "cc", "memory");
ret >>= 8;
return ret & 0xFF;
}
#endif