mirror of
https://github.com/86Box/probing-tools.git
synced 2026-02-25 04:45:33 -07:00
412 lines
8.7 KiB
C
412 lines
8.7 KiB
C
/*
|
|
* 86Box A hypervisor and IBM PC system emulator that specializes in
|
|
* running old operating systems and software designed for IBM
|
|
* PC systems and compatibles from 1981 through fairly recent
|
|
* system designs based on the PCI bus.
|
|
*
|
|
* This file is part of the 86Box Probing Tools distribution.
|
|
*
|
|
* ISA Plug and Play probing tool.
|
|
*
|
|
* Heavily based on iPXE drivers/bus/isapnp.c written by some
|
|
* of the authors identified below.
|
|
*
|
|
*
|
|
*
|
|
* Authors: RichardG, <richardg867@gmail.com>
|
|
* Timothy Legge, <tlegge@rogers.com>
|
|
* P.J.H.Fox, <fox@roestock.demon.co.uk>
|
|
* Michael Brown, <mbrown@fensystems.co.uk>
|
|
*
|
|
* Copyright 2022 RichardG.
|
|
* Copyright 2002-2003 Timothy Legge.
|
|
* Copyright 2001 P.J.H.Fox.
|
|
* Copyright Michael Brown.
|
|
*
|
|
* ┌──────────────────────────────────────────────────────────────┐
|
|
* │ This file is UTF-8 encoded. If this text is surrounded by │
|
|
* │ garbage, please tell your editor to open this file as UTF-8. │
|
|
* └──────────────────────────────────────────────────────────────┘
|
|
*/
|
|
#include <i86.h>
|
|
#include <inttypes.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include "clib.h"
|
|
|
|
|
|
typedef struct _card_ {
|
|
char id[8];
|
|
struct _card_ *next;
|
|
} card_t;
|
|
|
|
|
|
uint8_t buf[256], buf_pos;
|
|
uint16_t rd_data;
|
|
FILE *f;
|
|
card_t *first_card = NULL;
|
|
|
|
|
|
static inline void
|
|
io_delay()
|
|
{
|
|
delay(1);
|
|
}
|
|
|
|
|
|
static uint8_t
|
|
lfsr_advance(int lfsr, int bit) {
|
|
register uint8_t next;
|
|
next = lfsr >> 1;
|
|
next |= (((lfsr ^ next) ^ bit)) << 7;
|
|
return next;
|
|
}
|
|
|
|
|
|
static void
|
|
unlock()
|
|
{
|
|
int i, lfsr;
|
|
|
|
/* Trigger Wait for Key state. */
|
|
outb(0x279, 0x02);
|
|
outb(0xa79, 0x02);
|
|
|
|
/* Delay, then clear address port? */
|
|
io_delay();
|
|
outb(0x279, 0x00);
|
|
outb(0x279, 0x00);
|
|
|
|
/* Send key. */
|
|
lfsr = 0x6a;
|
|
for (i = 0; i < 32; i++) {
|
|
outb(0x279, lfsr);
|
|
lfsr = lfsr_advance(lfsr, 0);
|
|
}
|
|
}
|
|
|
|
|
|
static int
|
|
checksum(uint8_t *identifier)
|
|
{
|
|
int i, j, lfsr, byte;
|
|
|
|
lfsr = 0x6a;
|
|
for (i = 0; i < 8; i++) {
|
|
byte = identifier[i];
|
|
for (j = 0; j < 8; j++) {
|
|
lfsr = lfsr_advance(lfsr, byte);
|
|
byte >>= 1;
|
|
}
|
|
}
|
|
return lfsr;
|
|
}
|
|
|
|
|
|
static void
|
|
parse_id(uint8_t *identifier, char *id)
|
|
{
|
|
uint16_t vendor;
|
|
|
|
vendor = (identifier[0] << 8) | identifier[1];
|
|
sprintf(id, "%c%c%c%02X%02X",
|
|
'@' + ((vendor >> 10) & 0x1f),
|
|
'@' + ((vendor >> 5) & 0x1f),
|
|
'@' + (vendor & 0x1f),
|
|
identifier[2], identifier[3]);
|
|
}
|
|
|
|
|
|
static int
|
|
read_resource_data(uint8_t *byte)
|
|
{
|
|
uint8_t i;
|
|
|
|
/* Read byte. */
|
|
for (i = 0; i < 20; i++) {
|
|
/* Read only if ready. */
|
|
outb(0x279, 0x05);
|
|
if (inb(rd_data) & 0x01) {
|
|
outb(0x279, 0x04);
|
|
*byte = inb(rd_data);
|
|
break;
|
|
}
|
|
io_delay();
|
|
}
|
|
|
|
/* Return failure if the read timed out. */
|
|
if (i == 20) {
|
|
printf("\n> Read timed out at byte %d", ftell(f) + buf_pos + 1);
|
|
*byte = 0xff;
|
|
return 0;
|
|
}
|
|
|
|
/* Add byte to buffer. */
|
|
buf[buf_pos++] = *byte;
|
|
|
|
/* Flush buffer if full. */
|
|
if (!buf_pos && (fwrite(buf, sizeof(buf), 1, f) < 1)) {
|
|
printf("\n> File write failed");
|
|
return 0;
|
|
}
|
|
|
|
/* Return success. */
|
|
return 1;
|
|
}
|
|
|
|
|
|
static int
|
|
try_isolate()
|
|
{
|
|
char id[13];
|
|
uint8_t identifier[9], byte, i, j, seen_55aa, seen_life;
|
|
uint16_t data;
|
|
int csn;
|
|
card_t *card, *new_card;
|
|
|
|
printf("\rScanning on Read Data Port %04X...", rd_data);
|
|
|
|
/* Put all cards to Sleep. */
|
|
unlock();
|
|
|
|
/* Reset all CSNs. */
|
|
outb(0x279, 0x02);
|
|
outb(0xa79, 0x04);
|
|
io_delay();
|
|
io_delay();
|
|
|
|
/* Put all cards to Isolation. */
|
|
unlock();
|
|
outb(0x279, 0x03);
|
|
outb(0xa79, 0x00);
|
|
|
|
/* Set the read data port. */
|
|
outb(0x279, 0x00);
|
|
outb(0xa79, rd_data >> 2);
|
|
io_delay();
|
|
|
|
/* Isolate cards. */
|
|
csn = 0;
|
|
while (1) {
|
|
/* Put sleeping cards to Isolation. */
|
|
outb(0x279, 0x01);
|
|
io_delay();
|
|
|
|
/* Read serial identifier. */
|
|
memset(identifier, 0, sizeof(identifier));
|
|
seen_55aa = seen_life = 0;
|
|
for (i = 0; i < sizeof(identifier); i++) {
|
|
byte = 0;
|
|
for (j = 0; j < 8; j++) {
|
|
data = inb(rd_data);
|
|
io_delay();
|
|
data = (data << 8) | inb(rd_data);
|
|
io_delay();
|
|
byte >>= 1;
|
|
if (data != 0xffff) {
|
|
seen_life++;
|
|
if (data == 0x55aa) {
|
|
byte |= 0x80;
|
|
seen_55aa++;
|
|
}
|
|
}
|
|
}
|
|
identifier[i] = byte;
|
|
}
|
|
|
|
/* Stop if we didn't see any 55AA patterns. */
|
|
if (!seen_55aa) {
|
|
if (!csn && seen_life)
|
|
csn = -1;
|
|
break;
|
|
}
|
|
|
|
/* Output ID. */
|
|
if (csn == 0)
|
|
printf("\n");
|
|
parse_id(identifier, id);
|
|
printf("%s (%02X%02X%02X%02X)", id, identifier[7], identifier[6], identifier[5], identifier[4]);
|
|
|
|
/* Stop if the checksum is invalid. */
|
|
if (identifier[8] != checksum(identifier)) {
|
|
printf(" - bad checksum (expected %02X got %02X), trying another port\n",
|
|
checksum(identifier), identifier[8]);
|
|
csn = -1;
|
|
break;
|
|
}
|
|
|
|
/* Assign a CSN. */
|
|
csn++;
|
|
outb(0x279, 0x06);
|
|
outb(0xa79, csn);
|
|
io_delay();
|
|
|
|
/* Wake this card by its CSN to reset the resource data pointer. */
|
|
outb(0x279, 0x03);
|
|
outb(0xa79, csn);
|
|
io_delay();
|
|
|
|
/* Sanitize parsed PnP ID for filename purposes. */
|
|
for (i = 0; i < 3; i++) {
|
|
if (id[i] > 'Z')
|
|
id[i] = '_';
|
|
}
|
|
|
|
/* Determine this card's unique identifier. */
|
|
i = 0;
|
|
card = first_card;
|
|
while (card) {
|
|
/* Increment unique identifier for each card
|
|
already found with this sanitized parsed ID. */
|
|
if (!strcmp(card->id, id))
|
|
i++;
|
|
card = card->next;
|
|
}
|
|
new_card = malloc(sizeof(card_t));
|
|
strcpy(new_card->id, id);
|
|
new_card->next = NULL;
|
|
if (!first_card) {
|
|
first_card = new_card;
|
|
} else {
|
|
card = first_card;
|
|
while (card->next)
|
|
card = card->next;
|
|
card->next = new_card;
|
|
}
|
|
|
|
/* Generate a dump file name. */
|
|
sprintf(&id[7], "%c.BIN", 'A' + (i % 26));
|
|
printf(" - dumping to %s", id);
|
|
|
|
/* Open dump file. */
|
|
f = fopen(id, "wb");
|
|
if (f) {
|
|
/* Dump resource data, starting with the header. */
|
|
buf_pos = 0;
|
|
for (i = 0; i < 9; i++)
|
|
read_resource_data(&byte);
|
|
|
|
/* Now dump the resources. */
|
|
j = 0;
|
|
while (read_resource_data(&byte)) {
|
|
/* Determine the amount of bytes to skip depending on resource type. */
|
|
if (byte & 0x80) { /* large resource */
|
|
read_resource_data((uint8_t *) &data);
|
|
read_resource_data(((uint8_t *) &data) + 1);
|
|
|
|
/* Handle ANSI strings. */
|
|
byte &= 0x7f;
|
|
if (byte == 0x02) {
|
|
/* Output string. */
|
|
if (!j)
|
|
printf("\n>");
|
|
printf(" \"");
|
|
while (data--) {
|
|
read_resource_data(&byte);
|
|
if ((byte != 0x00) && (byte != '\r') && (byte != '\n'))
|
|
putchar(byte);
|
|
}
|
|
printf("\"");
|
|
data = 0;
|
|
}
|
|
} else { /* small resource */
|
|
data = byte & 0x07;
|
|
|
|
/* Handle some resource types. */
|
|
byte = (byte >> 3) & 0x0f;
|
|
if ((byte == 0x02) && (data >= 4)) { /* logical device */
|
|
/* Flag that we're in a logical device for string output purposes. */
|
|
j = 1;
|
|
|
|
/* Read logical device ID. */
|
|
for (i = 0; i < 4; i++)
|
|
read_resource_data(&identifier[i]);
|
|
data -= i;
|
|
|
|
/* Output logical device ID. */
|
|
parse_id(identifier, id);
|
|
printf("\n> %s", id);
|
|
} else if (byte == 0x0f) { /* end tag */
|
|
/* Read the rest of this resource (including the checksum), then stop. */
|
|
if (data == 0) /* just in case the checksum isn't covered */
|
|
data++;
|
|
while (data--)
|
|
read_resource_data(&byte);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Skip bytes. */
|
|
while (data--)
|
|
read_resource_data(&byte);
|
|
}
|
|
|
|
/* Flush buffer if not empty. */
|
|
if (buf_pos) {
|
|
if (fwrite(buf, buf_pos, 1, f) < 1)
|
|
printf("\n> File write failed");
|
|
}
|
|
|
|
/* Finish the dump. */
|
|
fclose(f);
|
|
printf("\n");
|
|
} else {
|
|
printf("\n> File creation failed\n");
|
|
}
|
|
|
|
/* Put this card to Sleep and other cards to Isolation. */
|
|
outb(0x279, 0x03);
|
|
outb(0xa79, 0x00);
|
|
io_delay();
|
|
}
|
|
|
|
/* Put all cards to Wait for Key. */
|
|
outb(0x279, 0x02);
|
|
outb(0xa79, 0x02);
|
|
|
|
/* Return card count / maximum CSN. */
|
|
return csn;
|
|
}
|
|
|
|
|
|
int
|
|
main(int argc, char **argv)
|
|
{
|
|
int max_csn;
|
|
card_t *card;
|
|
|
|
/* Disable stdout buffering. */
|
|
term_unbuffer_stdout();
|
|
|
|
/* Try read data ports until a good one is found. iPXE tries
|
|
[213:3FF] with a hole at [280:380] for whatever safety reason. */
|
|
max_csn = -1;
|
|
for (rd_data = 0x213; rd_data <= 0x3ff; rd_data += 16) {
|
|
if ((rd_data >= 0x280) && (rd_data <= 0x380))
|
|
continue;
|
|
|
|
max_csn = try_isolate();
|
|
if (max_csn >= 0)
|
|
break;
|
|
}
|
|
|
|
/* Free the card list. */
|
|
while (first_card) {
|
|
card = first_card->next;
|
|
free(first_card);
|
|
first_card = card;
|
|
}
|
|
|
|
/* Nothing returned. */
|
|
if (max_csn <= 0) {
|
|
if (max_csn < 0)
|
|
printf("\nNo good Read Data Ports found!\n");
|
|
else
|
|
printf("\nNo devices found!\n");
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|