Merge pull request #30 from joevt/master

more fixes
This commit is contained in:
Maxim Poliakovski 2022-08-23 02:56:26 +02:00 committed by GitHub
commit 141a276c5b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
43 changed files with 858 additions and 228 deletions

View File

@ -12,7 +12,7 @@ This program aims to not only improve upon what Sheepshaver, PearPC, and other P
## Implemented Features
This emulator has a debugging environment, complete with a disassembler. We also have implemented enough to allow OpenFirmware to boot, going so far as to allow audio playback of the boot-up jingles.
This emulator has a debugging environment, complete with a disassembler. We also have implemented enough to allow Open Firmware to boot, going so far as to allow audio playback of the boot-up jingles.
## How to Use
@ -52,7 +52,7 @@ You need to install development tools first.
At least, a C++11 compliant compiler and [CMake](https://cmake.org) are required.
You will also have to recursive clone or run
You will also have to recursive clone or run
```
git submodule update --init --recursive
```

View File

@ -27,6 +27,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
#include <map>
#include <memory>
#include <sstream>
#include <cstring>
#include <stdio.h>
#include <string>
#include "../cpu/ppc/ppcdisasm.h"
@ -36,6 +37,13 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
#include "memaccess.h"
#include "utils/profiler.h"
#ifdef _WIN32
#else
#include <signal.h>
#include <termios.h>
#include <unistd.h>
#endif
#ifdef ENABLE_68K_DEBUGGER // optionally defined in CMakeLists.txt
#include <capstone/capstone.h>
#endif
@ -368,6 +376,25 @@ static void print_gprs() {
}
}
static struct sigaction old_act_sigint, new_act_sigint;
static struct sigaction old_act_sigterm, new_act_sigterm;
static struct termios orig_termios;
static void mysig_handler(int signum)
{
// restore original terminal state
tcsetattr(STDIN_FILENO, TCSANOW, &orig_termios);
// restore original signal handler for SIGINT
signal(SIGINT, old_act_sigint.sa_handler);
signal(SIGTERM, old_act_sigterm.sa_handler);
LOG_F(INFO, "Old terminal state restored, SIG#=%d", signum);
// re-post signal
raise(signum);
}
void enter_debugger() {
string inp, cmd, addr_str, expr_str, reg_expr, last_cmd, reg_value_str,
inst_string, inst_num_str, profile_name, sub_cmd;
@ -587,6 +614,53 @@ void enter_debugger() {
if (ofnvram->init())
continue;
ofnvram->setenv(var_name, value);
} else if (cmd == "nvedit") {
cout << "===== press CNTRL-C to save =====" << endl;
// save original terminal state
tcgetattr(STDIN_FILENO, &orig_termios);
struct termios new_termios = orig_termios;
new_termios.c_cflag &= ~(CSIZE | PARENB);
new_termios.c_cflag |= CS8;
new_termios.c_lflag &= ~(ISIG | NOFLSH | ICANON | ECHOCTL);
new_termios.c_lflag |= NOFLSH | ECHONL;
// new_termios.c_iflag &= ~(ICRNL | IGNCR);
// new_termios.c_iflag |= INLCR;
// new_termios.c_oflag &= ~(ONOCR | ONLCR);
// new_termios.c_oflag |= OPOST | OCRNL | ONLRET;
tcsetattr(STDIN_FILENO, TCSANOW, &new_termios);
// save original signal handler for SIGINT
// then redirect SIGINT to new handler
memset(&new_act_sigint, 0, sizeof(new_act_sigint));
new_act_sigint.sa_handler = mysig_handler;
sigaction(SIGINT, &new_act_sigint, &old_act_sigint);
// save original signal handler for SIGTERM
// then redirect SIGTERM to new handler
memset(&new_act_sigterm, 0, sizeof(new_act_sigterm));
new_act_sigterm.sa_handler = mysig_handler;
sigaction(SIGTERM, &new_act_sigterm, &old_act_sigterm);
getline(cin, inp, '\x03');
// restore original terminal state
tcsetattr(STDIN_FILENO, TCSANOW, &orig_termios);
// restore original signal handler for SIGINT
signal(SIGINT, old_act_sigint.sa_handler);
// restore original signal handler for SIGTERM
signal(SIGTERM, old_act_sigterm.sa_handler);
if (ofnvram->init())
continue;
ofnvram->setenv("nvramrc", inp);
} else {
cout << "Unknown command: " << cmd << endl;
continue;

View File

@ -55,12 +55,12 @@ public:
};
~NubusMacID() = default;
uint32_t read(uint32_t reg_start, uint32_t offset, int size) {
uint32_t read(uint32_t rgn_start, uint32_t offset, int size) {
return (offset < 4 ? this->id[offset] : 0);
};
/* not writable */
void write(uint32_t reg_start, uint32_t offset, uint32_t value, int size) {};
void write(uint32_t rgn_start, uint32_t offset, uint32_t value, int size) {};
private:
uint8_t id[4];
@ -80,12 +80,12 @@ public:
};
~GossamerID() = default;
uint32_t read(uint32_t reg_start, uint32_t offset, int size) {
uint32_t read(uint32_t rgn_start, uint32_t offset, int size) {
return ((!offset && size == 2) ? this->id : 0);
};
/* not writable */
void write(uint32_t reg_start, uint32_t offset, uint32_t value, int size) {};
void write(uint32_t rgn_start, uint32_t offset, uint32_t value, int size) {};
private:
uint16_t id;

View File

@ -31,8 +31,8 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
class MMIODevice : public HWComponent {
public:
MMIODevice() = default;
virtual uint32_t read(uint32_t reg_start, uint32_t offset, int size) = 0;
virtual void write(uint32_t reg_start, uint32_t offset, uint32_t value, int size) = 0;
virtual uint32_t read(uint32_t rgn_start, uint32_t offset, int size) = 0;
virtual void write(uint32_t rgn_start, uint32_t offset, uint32_t value, int size) = 0;
virtual ~MMIODevice() = default;
};

View File

@ -19,7 +19,7 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
/** @file Utilities for working with the Apple OpenFirmware NVRAM partition. */
/** @file Utilities for working with the Apple Open Firmware NVRAM partition. */
#include <devices/common/ofnvram.h>
#include <endianswap.h>
@ -37,6 +37,19 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
using namespace std;
static uint32_t str2env(string& num_str) {
try {
return stoul(num_str, NULL, 0);
} catch (invalid_argument& exc) {
try {
string num_str2 = string("0x") + num_str;
return std::stoul(num_str2, NULL, 0);
} catch (invalid_argument& exc) {
throw invalid_argument(string("Cannot convert ") + num_str);
}
}
}
int OfNvramUtils::init()
{
this->nvram_obj = dynamic_cast<NVram*>(gMachineObj->get_comp_by_name("NVRAM"));
@ -146,7 +159,7 @@ void OfNvramUtils::printenv()
int i;
if (!this->validate()) {
cout << "Invalid OpenFirmware partition content!" << endl;
cout << "Invalid Open Firmware partition content!" << endl;
return;
}
@ -174,10 +187,9 @@ void OfNvramUtils::printenv()
case OF_VAR_TYPE_STR:
uint16_t str_offset = READ_WORD_BE_A(&this->buf[offset]) - OF_NVRAM_OFFSET;
uint16_t str_len = READ_WORD_BE_A(&this->buf[offset+2]);
if (str_len) {
char value[32] = "";
std::memcpy(value, (char *)&(this->buf[str_offset]), str_len);
cout << value;
for (i = 0; i < str_len; i++) {
char ch = *(char *)&(this->buf[str_offset + i]);
if (ch == '\x0D') cout << endl; else cout << ch;
}
cout << endl;
}
@ -191,7 +203,7 @@ void OfNvramUtils::setenv(string var_name, string value)
int i, flag;
if (!this->validate()) {
cout << "Invalid OpenFirmware partition content!" << endl;
cout << "Invalid Open Firmware partition content!" << endl;
return;
}
@ -221,7 +233,7 @@ void OfNvramUtils::setenv(string var_name, string value)
}
}
// see if one of the stanard properties should be changed
// see if one of the standard properties should be changed
if (of_vars.find(var_name) == of_vars.end()) {
cout << "Attempt to change unknown variable " << var_name << endl;
return;
@ -231,47 +243,63 @@ void OfNvramUtils::setenv(string var_name, string value)
auto offset = std::get<1>(of_vars.at(var_name));
if (type == OF_VAR_TYPE_INT) {
cout << "Changing of integer variables not supported yet" << endl;
} else {
uint16_t str_len = READ_WORD_BE_A(&this->buf[offset+2]);
if (value.length() > str_len) {
// we're going to allocate additional space
// for the new string in the heap between here and top.
// TODO: implement removal of dead strings and heap compaction
OfNvramHdr *hdr = (OfNvramHdr *)&this->buf[0];
uint16_t here = READ_WORD_BE_A(&hdr->here);
uint16_t top = READ_WORD_BE_A(&hdr->top);
// check if there is enough space in the heap for the new string
if ((top - value.length()) < here) {
cout << "No room in the heap!" << endl;
return;
}
// allocate required space by lowering top
top -= value.length();
i = 0;
// copy new string into NVRAM buffer char by char
for(char& ch : value) {
this->buf[top + i - OF_NVRAM_OFFSET] = ch;
i++;
}
// stuff new values into the variable state
WRITE_WORD_BE_A(&this->buf[offset+0], top);
WRITE_WORD_BE_A(&this->buf[offset+2], value.length());
// update partition header
WRITE_WORD_BE_A(&hdr->top, top);
// update physical NVRAM
this->update_partition();
cout << " ok" << endl; // mimic Forth
uint32_t num;
try {
num = str2env(value);
} catch (invalid_argument& exc) {
cout << exc.what() << endl;
return;
} else {
// TODO: replace existing string
}
WRITE_DWORD_BE_A(&this->buf[offset], num);
this->update_partition();
cout << " ok" << endl; // mimic Forth
} else {
uint16_t str_offset = READ_WORD_BE_A(&this->buf[offset]);
uint16_t str_len = READ_WORD_BE_A(&this->buf[offset+2]);
OfNvramHdr *hdr = (OfNvramHdr *)&this->buf[0];
uint16_t here = READ_WORD_BE_A(&hdr->here);
uint16_t top = READ_WORD_BE_A(&hdr->top);
// check if there is enough space in the heap for the new string
// the heap is grown down from offset 0x2000 and cannot be lower than here (0x185c)
uint16_t new_top = top + str_len - value.length();
if (new_top < here) {
cout << "No room in the heap!" << endl;
return;
}
// remove the old string
std::memmove(&this->buf[top + str_len - OF_NVRAM_OFFSET], &this->buf[top - OF_NVRAM_OFFSET], str_offset - top);
for (auto& var : of_vars) {
auto type = std::get<0>(var.second);
auto offset = std::get<1>(var.second);
if (type == OF_VAR_TYPE_STR) {
uint16_t i_str_offset = READ_WORD_BE_A(&this->buf[offset]);
if (i_str_offset < str_offset) {
WRITE_WORD_BE_A(&this->buf[offset], i_str_offset + str_len);
}
}
}
top = new_top;
// copy new string into NVRAM buffer char by char
i = 0;
for(char& ch : value) {
this->buf[top + i - OF_NVRAM_OFFSET] = ch == '\x0A' ? '\x0D' : ch;
i++;
}
// stuff new values into the variable state
WRITE_WORD_BE_A(&this->buf[offset+0], top);
WRITE_WORD_BE_A(&this->buf[offset+2], value.length());
// update partition header
WRITE_WORD_BE_A(&hdr->top, top);
// update physical NVRAM
this->update_partition();
cout << " ok" << endl; // mimic Forth
return;
}
}

View File

@ -24,7 +24,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
#include <devices/common/nvram.h>
/** @file Utilities for working with the Apple OpenFirmware NVRAM partition. */
/** @file Utilities for working with the Apple Open Firmware NVRAM partition. */
#define OF_NVRAM_OFFSET 0x1800
#define OF_NVRAM_SIG 0x1275
@ -36,12 +36,12 @@ enum {
};
typedef struct {
uint16_t sig; // partition signature (= 0x1275)
uint8_t version; // header version
uint8_t num_pages; // number of memory pages
uint16_t checksum; // partition checksum
uint16_t here; // offset to the next free byte (bottom-up)
uint16_t top; // offset to the last free byte (top-down)
uint16_t sig; // >nv.1275 partition signature (= 0x1275)
uint8_t version; // >nv.version header version (= 5)
uint8_t num_pages; // >nv.pages number of memory pages (= 8 pages 0x100 bytes each)
uint16_t checksum; // >nv.checksum partition checksum
uint16_t here; // >nv.here offset to the next free byte (offset of after last string length; = 0x185c)
uint16_t top; // >nv.top offset to the last free byte (offset of string with lowest offset; < 0x2000)
} OfNvramHdr;
class OfNvramUtils {

View File

@ -31,6 +31,15 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
#include <cinttypes>
const int MultiplyDeBruijnBitPosition2[] =
{
0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8,
31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9
};
/** finds the position of the bit that is set */
#define WHAT_BIT_SET(val) (MultiplyDeBruijnBitPosition2[(uint32_t)(val * 0x077CB531U) >> 27])
Bandit::Bandit(int bridge_num, std::string name) : PCIHost(), PCIDevice(name)
{
supports_types(HWCompType::PCI_HOST | HWCompType::PCI_DEV);
@ -63,6 +72,8 @@ Bandit::Bandit(int bridge_num, std::string name) : PCIHost(), PCIDevice(name)
// that correspond to the 32MB assigned PCI address space of this Bandit.
// This initialization is implied by the device functionality.
this->addr_mask = 3 << ((bridge_num & 3) * 2);
this->name = name;
}
uint32_t Bandit::pci_cfg_read(uint32_t reg_offs, uint32_t size)
@ -100,26 +111,38 @@ void Bandit::pci_cfg_write(uint32_t reg_offs, uint32_t value, uint32_t size)
}
}
uint32_t Bandit::read(uint32_t reg_start, uint32_t offset, int size)
uint32_t Bandit::read(uint32_t rgn_start, uint32_t offset, int size)
{
int fun_num;
int bus_num, dev_num, fun_num;
uint8_t reg_offs;
uint32_t result, idsel;
if (offset & BANDIT_CONFIG_SPACE) {
if (offset & 0x00400000) {
fun_num = (this->config_addr >> 8) & 7;
reg_offs = this->config_addr & 0xFCU;
// access to the CONFIG_DATA pseudo-register causes a Config Cycle
if (this->config_addr & BANDIT_CAR_TYPE) {
LOG_F(WARNING, "%s: config cycle type 1 not supported yet", this->name.c_str());
bus_num = (this->config_addr >> 16) & 255;
dev_num = (this->config_addr >> 11) & 31;
LOG_F(
WARNING, "%s: read config cycle type 1 not supported yet %02x:%02x.%x @%02x.%c",
this->name.c_str(), bus_num, dev_num, fun_num, reg_offs + (offset & 3),
size == 4 ? 'l' : size == 2 ? 'w' : size == 1 ? 'b' : '0' + size
);
return 0;
}
idsel = (this->config_addr >> 11) & 0x1FFFFFU;
fun_num = (this->config_addr >> 8) & 7;
reg_offs = this->config_addr & 0xFCU;
idsel = (this->config_addr >> 11) & 0x1FFFFFU;
if (!SINGLE_BIT_SET(idsel)) {
LOG_F(ERROR, "%s: invalid IDSEL=0x%X passed", this->name.c_str(), idsel);
LOG_F(
ERROR, "%s: read invalid IDSEL=0x%X config:0x%X ??:??.%x? @%02x?.%c",
this->name.c_str(), idsel, this->config_addr,
fun_num, reg_offs + (offset & 3),
size == 4 ? 'l' : size == 2 ? 'w' : size == 1 ? 'b' : '0' + size
);
return 0;
}
@ -129,11 +152,12 @@ uint32_t Bandit::read(uint32_t reg_start, uint32_t offset, int size)
if (this->dev_map.count(idsel)) {
result = this->dev_map[idsel]->pci_cfg_read(reg_offs, size);
} else {
dev_num = WHAT_BIT_SET(idsel) + 11;
LOG_F(
ERROR,
"%s err: read attempt from non-existing PCI device %d",
this->name.c_str(),
idsel);
ERROR, "%s err: read attempt from non-existing PCI device ??:%02x.%x @%02x.%c",
this->name.c_str(), dev_num, fun_num, reg_offs + (offset & 3),
size == 4 ? 'l' : size == 2 ? 'w' : size == 1 ? 'b' : '0' + size
);
return 0xFFFFFFFFUL; // PCI spec §6.1
}
}
@ -154,26 +178,38 @@ uint32_t Bandit::read(uint32_t reg_start, uint32_t offset, int size)
return result;
}
void Bandit::write(uint32_t reg_start, uint32_t offset, uint32_t value, int size)
void Bandit::write(uint32_t rgn_start, uint32_t offset, uint32_t value, int size)
{
int fun_num;
int bus_num, dev_num, fun_num;
uint8_t reg_offs;
uint32_t idsel;
if (offset & BANDIT_CONFIG_SPACE) {
if (offset & 0x00400000) {
fun_num = (this->config_addr >> 8) & 7;
reg_offs = this->config_addr & 0xFCU;
// access to the CONFIG_DATA pseudo-register causes a Config Cycle
if (this->config_addr & BANDIT_CAR_TYPE) {
LOG_F(WARNING, "%s: config cycle type 1 not supported yet", this->name.c_str());
bus_num = (this->config_addr >> 16) & 255;
dev_num = (this->config_addr >> 11) & 31;
LOG_F(
WARNING, "%s: write config cycle type 1 not supported yet %02x:%02x.%x @%02x.%c = %0*x",
this->name.c_str(), bus_num, dev_num, fun_num, reg_offs + (offset & 3),
size == 4 ? 'l' : size == 2 ? 'w' : size == 1 ? 'b' : '0' + size, size * 2, value
);
return;
}
idsel = (this->config_addr >> 11) & 0x1FFFFFU;
fun_num = (this->config_addr >> 8) & 7;
reg_offs = this->config_addr & 0xFCU;
idsel = (this->config_addr >> 11) & 0x1FFFFFU;
if (!SINGLE_BIT_SET(idsel)) {
LOG_F(ERROR, "%s: invalid IDSEL=0x%X passed", this->name.c_str(), idsel);
LOG_F(
ERROR, "%s: write invalid IDSEL=0x%X config:0x%X ??:??.%x? @%02x?.%c = %0*x",
this->name.c_str(), idsel, this->config_addr,
fun_num, reg_offs + (offset & 3),
size == 4 ? 'l' : size == 2 ? 'w' : size == 1 ? 'b' : '0' + size, size * 2, value
);
return;
}
@ -185,11 +221,12 @@ void Bandit::write(uint32_t reg_start, uint32_t offset, uint32_t value, int size
if (this->dev_map.count(idsel)) {
this->dev_map[idsel]->pci_cfg_write(reg_offs, value, size);
} else {
dev_num = WHAT_BIT_SET(idsel) + 11;
LOG_F(
ERROR,
"%s err: write attempt to non-existing PCI device %d",
this->name.c_str(),
idsel);
ERROR, "%s err: write attempt to non-existing PCI device ??:%02x.%x @%02x.%c = %0*x",
this->name.c_str(), dev_num, fun_num, reg_offs + (offset & 3),
size == 4 ? 'l' : size == 2 ? 'w' : size == 1 ? 'b' : '0' + size, size * 2, value
);
}
} else {
this->config_addr = BYTESWAP_32(value);
@ -247,31 +284,50 @@ Chaos::Chaos(std::string name) : PCIHost()
this->name = name;
}
uint32_t Chaos::read(uint32_t reg_start, uint32_t offset, int size)
uint32_t Chaos::read(uint32_t rgn_start, uint32_t offset, int size)
{
int fun_num;
int bus_num, dev_num, fun_num;
uint8_t reg_offs;
uint32_t result, idsel;
if (offset & BANDIT_CONFIG_SPACE) {
if (offset & 0x00400000) {
idsel = (this->config_addr >> 11) & 0x1FFFFFU;
fun_num = (this->config_addr >> 8) & 7;
fun_num = (this->config_addr >> 8) & 7;
reg_offs = this->config_addr & 0xFCU;
// access to the CONFIG_DATA pseudo-register causes a Config Cycle
if (this->config_addr & BANDIT_CAR_TYPE) {
bus_num = (this->config_addr >> 16) & 255;
dev_num = (this->config_addr >> 11) & 31;
LOG_F(
WARNING, "%s: read config cycle type 1 not supported yet %02x:%02x.%x @%02x.%c",
this->name.c_str(), bus_num, dev_num, fun_num, reg_offs + (offset & 3),
size == 4 ? 'l' : size == 2 ? 'w' : size == 1 ? 'b' : '0' + size
);
return 0;
}
idsel = (this->config_addr >> 11) & 0x1FFFFFU;
if (!SINGLE_BIT_SET(idsel)) {
LOG_F(ERROR, "%s: invalid IDSEL=0x%X passed", this->name.c_str(), idsel);
LOG_F(
ERROR, "%s: read invalid IDSEL=0x%X config:0x%X ??:??.%x? @%02x?.%c",
this->name.c_str(), idsel, this->config_addr,
fun_num, reg_offs + (offset & 3),
size == 4 ? 'l' : size == 2 ? 'w' : size == 1 ? 'b' : '0' + size
);
return 0;
}
if (this->dev_map.count(idsel)) {
result = this->dev_map[idsel]->pci_cfg_read(reg_offs, size);
} else {
dev_num = WHAT_BIT_SET(idsel) + 11;
LOG_F(
ERROR,
"%s err: read attempt from non-existing VCI device %d",
this->name.c_str(),
idsel);
ERROR, "%s err: read attempt from non-existing VCI device ??:%02x.%x @%02x.%c",
this->name.c_str(), dev_num, fun_num, reg_offs + (offset & 3),
size == 4 ? 'l' : size == 2 ? 'w' : size == 1 ? 'b' : '0' + size
);
return 0xFFFFFFFFUL; // PCI spec §6.1
}
} else {
@ -284,37 +340,50 @@ uint32_t Chaos::read(uint32_t reg_start, uint32_t offset, int size)
return result;
}
void Chaos::write(uint32_t reg_start, uint32_t offset, uint32_t value, int size)
void Chaos::write(uint32_t rgn_start, uint32_t offset, uint32_t value, int size)
{
int fun_num;
int bus_num, dev_num, fun_num;
uint8_t reg_offs;
uint32_t idsel;
if (offset & BANDIT_CONFIG_SPACE) {
if (offset & 0x00400000) {
fun_num = (this->config_addr >> 8) & 7;
reg_offs = this->config_addr & 0xFCU;
// access to the CONFIG_DATA pseudo-register causes a Config Cycle
if (this->config_addr & BANDIT_CAR_TYPE) {
LOG_F(WARNING, "%s: config cycle type 1 not supported yet", this->name.c_str());
bus_num = (this->config_addr >> 16) & 255;
dev_num = (this->config_addr >> 11) & 31;
LOG_F(
WARNING, "%s: write config cycle type 1 not supported yet %02x:%02x.%x @%02x.%c = %0*x",
this->name.c_str(), bus_num, dev_num, fun_num, reg_offs + (offset & 3),
size == 4 ? 'l' : size == 2 ? 'w' : size == 1 ? 'b' : '0' + size, size * 2, value
);
return;
}
idsel = (this->config_addr >> 11) & 0x1FFFFFU;
fun_num = (this->config_addr >> 8) & 7;
reg_offs = this->config_addr & 0xFCU;
idsel = (this->config_addr >> 11) & 0x1FFFFFU;
if (!SINGLE_BIT_SET(idsel)) {
LOG_F(ERROR, "%s: invalid IDSEL=0x%X passed", this->name.c_str(), idsel);
LOG_F(
ERROR, "%s: write invalid IDSEL=0x%X config:0x%X ??:??.%x? @%02x?.%c = %0*x",
this->name.c_str(), idsel, this->config_addr,
fun_num, reg_offs + (offset & 3),
size == 4 ? 'l' : size == 2 ? 'w' : size == 1 ? 'b' : '0' + size, size * 2, value
);
return;
}
if (this->dev_map.count(idsel)) {
this->dev_map[idsel]->pci_cfg_write(reg_offs, value, size);
} else {
dev_num = WHAT_BIT_SET(idsel) + 11;
LOG_F(
ERROR,
"%s err: write attempt to non-existing VCI device %d",
this->name.c_str(),
idsel);
ERROR, "%s err: write attempt to non-existing VCI device ??:%02x.%x @%02x.%c = %0*x",
this->name.c_str(), dev_num, fun_num, reg_offs + (offset & 3),
size == 4 ? 'l' : size == 2 ? 'w' : size == 1 ? 'b' : '0' + size, size * 2, value
);
}
} else {
this->config_addr = BYTESWAP_32(value);

View File

@ -63,8 +63,8 @@ public:
void pci_cfg_write(uint32_t reg_offs, uint32_t value, uint32_t size);
// MMIODevice methods
uint32_t read(uint32_t reg_start, uint32_t offset, int size);
void write(uint32_t reg_start, uint32_t offset, uint32_t value, int size);
uint32_t read(uint32_t rgn_start, uint32_t offset, int size);
void write(uint32_t rgn_start, uint32_t offset, uint32_t value, int size);
protected:
void verbose_address_space();
@ -90,8 +90,8 @@ public:
};
// MMIODevice methods
uint32_t read(uint32_t reg_start, uint32_t offset, int size);
void write(uint32_t reg_start, uint32_t offset, uint32_t value, int size);
uint32_t read(uint32_t rgn_start, uint32_t offset, int size);
void write(uint32_t rgn_start, uint32_t offset, uint32_t value, int size);
private:
std::string name;

View File

@ -33,7 +33,7 @@ PCIDevice::PCIDevice(std::string name)
{
this->pci_name = name;
this->pci_rd_stat = []() { return 0; };
this->pci_rd_stat = [this]() { return this->status; };
this->pci_rd_cmd = [this]() { return this->command; };
this->pci_rd_bist = []() { return 0; };
this->pci_rd_lat_timer = [this]() { return this->lat_timer; };
@ -83,9 +83,15 @@ uint32_t PCIDevice::pci_cfg_read(uint32_t reg_offs, uint32_t size)
case PCI_CFG_DWORD_15:
result = (max_lat << 24) | (min_gnt << 16) | (irq_pin << 8) | irq_line;
break;
case PCI_CFG_CAP_PTR:
result = cap_ptr;
break;
default:
LOG_F(WARNING, "%s: attempt to read from reserved/unimplemented register %d",
this->pci_name.c_str(), reg_offs);
LOG_F(
WARNING, "%s: attempt to read from reserved/unimplemented register @%02x.%c",
this->pci_name.c_str(), reg_offs,
size == 4 ? 'l' : size == 2 ? 'w' : size == 1 ? 'b' : '0' + size
);
return 0;
}
@ -147,8 +153,11 @@ void PCIDevice::pci_cfg_write(uint32_t reg_offs, uint32_t value, uint32_t size)
this->irq_line = data >> 24;
break;
default:
LOG_F(WARNING, "%s: attempt to write to reserved/unimplemented register %d",
this->pci_name.c_str(), reg_offs);
LOG_F(
WARNING, "%s: attempt to write to reserved/unimplemented register @%02x.%c = %0*x",
this->pci_name.c_str(), reg_offs,
size == 4 ? 'l' : size == 2 ? 'w' : size == 1 ? 'b' : '0' + size, size * 2, value
);
}
}
@ -178,14 +187,14 @@ int PCIDevice::attach_exp_rom_image(const std::string img_path)
// determine image size
img_file.seekg(0, std::ios::end);
this->exp_rom_size = img_file.tellg();
uint32_t exp_rom_image_size = img_file.tellg();
// verify PCI struct offset
uint32_t pci_struct_offset = 0;
img_file.seekg(0x18, std::ios::beg);
img_file.read((char *)&pci_struct_offset, sizeof(pci_struct_offset));
if (pci_struct_offset > this->exp_rom_size) {
if (pci_struct_offset > exp_rom_image_size) {
throw std::runtime_error("invalid PCI structure offset");
}
@ -197,13 +206,22 @@ int PCIDevice::attach_exp_rom_image(const std::string img_path)
throw std::runtime_error("unexpected PCI struct signature");
}
// find minimum rom size for the rom file (power of 2 >= 0x800)
for (this->exp_rom_size = 1 << 11; this->exp_rom_size < exp_rom_image_size; this->exp_rom_size <<= 1) {}
// ROM image ok - go ahead and load it
this->exp_rom_data = std::unique_ptr<uint8_t[]> (new uint8_t[this->exp_rom_size]);
img_file.seekg(0, std::ios::beg);
img_file.read((char *)this->exp_rom_data.get(), this->exp_rom_size);
img_file.read((char *)this->exp_rom_data.get(), exp_rom_image_size);
memset(&this->exp_rom_data[exp_rom_image_size], 0xff, this->exp_rom_size - exp_rom_image_size);
if (exp_rom_image_size == this->exp_rom_size) {
LOG_F(INFO, "%s: loaded expansion rom (%d bytes).", this->pci_name.c_str(), this->exp_rom_size);
}
else {
LOG_F(WARNING, "%s: loaded expansion rom (%d bytes adjusted to %d bytes).", this->pci_name.c_str(), exp_rom_image_size, this->exp_rom_size);
}
// align ROM image size on a 2KB boundary and initialize ROM config
this->exp_rom_size = (this->exp_rom_size + 0x7FFU) & 0xFFFFF800UL;
this->exp_bar_cfg = ~(this->exp_rom_size - 1);
}
catch (const std::exception& exc) {

View File

@ -45,6 +45,8 @@ enum {
PCI_CFG_CIS_PTR = 0x28, // Cardbus CIS Pointer
PCI_CFG_SUBSYS_ID = 0x2C, // Subsysten IDs
PCI_CFG_ROM_BAR = 0x30, // expansion ROM base address
PCI_CFG_CAP_PTR = 0x34, // capabilities pointer
PCI_CFG_DWORD_14 = 0x38, // reserved
PCI_CFG_DWORD_15 = 0x3C, // Max_Lat, Min_Gnt, Int_Pin and Int_Line registers
};
@ -117,6 +119,7 @@ protected:
uint8_t cache_ln_sz = 0; // cache line size
uint16_t subsys_id = 0;
uint16_t subsys_vndr = 0;
uint8_t cap_ptr = 0;
uint8_t max_lat = 0;
uint8_t min_gnt = 0;
uint8_t irq_pin = 0;

View File

@ -39,7 +39,7 @@ uint8_t MESHController::read(uint8_t reg_offset)
LOG_F(INFO, "MESH: read from MeshID register");
return this->chip_id; // tell them who we are
default:
LOG_F(WARNING, "MESH: read from unimplemented register at offset %d", reg_offset);
LOG_F(WARNING, "MESH: read from unimplemented register at offset 0x%x", reg_offset);
}
return 0;
@ -67,7 +67,7 @@ void MESHController::write(uint8_t reg_offset, uint8_t value)
LOG_F(INFO, "MESH: write 0x%02X to SyncParms register", value);
break;
default:
LOG_F(WARNING, "MESH: write to unimplemented register at offset %d", reg_offset);
LOG_F(WARNING, "MESH: write to unimplemented register at offset 0x%x", reg_offset);
}
}

View File

@ -101,7 +101,7 @@ int AMIC::device_postinit()
return 0;
}
uint32_t AMIC::read(uint32_t reg_start, uint32_t offset, int size)
uint32_t AMIC::read(uint32_t rgn_start, uint32_t offset, int size)
{
uint32_t phase_val;
@ -180,7 +180,7 @@ uint32_t AMIC::read(uint32_t reg_start, uint32_t offset, int size)
return 0;
}
void AMIC::write(uint32_t reg_start, uint32_t offset, uint32_t value, int size)
void AMIC::write(uint32_t rgn_start, uint32_t offset, uint32_t value, int size)
{
uint32_t mask;

View File

@ -192,8 +192,8 @@ public:
int device_postinit();
/* MMIODevice methods */
uint32_t read(uint32_t reg_start, uint32_t offset, int size);
void write(uint32_t reg_start, uint32_t offset, uint32_t value, int size);
uint32_t read(uint32_t rgn_start, uint32_t offset, int size);
void write(uint32_t rgn_start, uint32_t offset, uint32_t value, int size);
// InterruptCtrl methods
uint32_t register_dev_int(IntSrc src_id);

View File

@ -94,7 +94,7 @@ void GrandCentral::notify_bar_change(int bar_num)
}
}
uint32_t GrandCentral::read(uint32_t reg_start, uint32_t offset, int size)
uint32_t GrandCentral::read(uint32_t rgn_start, uint32_t offset, int size)
{
if (offset & 0x10000) { // Device register space
unsigned subdev_num = (offset >> 12) & 0xF;
@ -160,7 +160,7 @@ uint32_t GrandCentral::read(uint32_t reg_start, uint32_t offset, int size)
return 0;
}
void GrandCentral::write(uint32_t reg_start, uint32_t offset, uint32_t value, int size)
void GrandCentral::write(uint32_t rgn_start, uint32_t offset, uint32_t value, int size)
{
if (offset & 0x10000) { // Device register space
unsigned subdev_num = (offset >> 12) & 0xF;

View File

@ -128,7 +128,7 @@ void HeathrowIC::dma_write(uint32_t offset, uint32_t value, int size) {
}
uint32_t HeathrowIC::read(uint32_t reg_start, uint32_t offset, int size) {
uint32_t HeathrowIC::read(uint32_t rgn_start, uint32_t offset, int size) {
uint32_t res = 0;
LOG_F(9, "%s: reading from offset %x", this->name.c_str(), offset);
@ -172,7 +172,7 @@ uint32_t HeathrowIC::read(uint32_t reg_start, uint32_t offset, int size) {
return res;
}
void HeathrowIC::write(uint32_t reg_start, uint32_t offset, uint32_t value, int size) {
void HeathrowIC::write(uint32_t rgn_start, uint32_t offset, uint32_t value, int size) {
LOG_F(9, "%s: writing to offset %x", this->name.c_str(), offset);
unsigned sub_addr = (offset >> 12) & 0x7F;

View File

@ -100,8 +100,8 @@ public:
}
// MMIO device methods
uint32_t read(uint32_t reg_start, uint32_t offset, int size);
void write(uint32_t reg_start, uint32_t offset, uint32_t value, int size);
uint32_t read(uint32_t rgn_start, uint32_t offset, int size);
void write(uint32_t rgn_start, uint32_t offset, uint32_t value, int size);
// InterruptCtrl methods
uint32_t register_dev_int(IntSrc src_id);
@ -182,8 +182,8 @@ public:
}
// MMIO device methods
uint32_t read(uint32_t reg_start, uint32_t offset, int size);
void write(uint32_t reg_start, uint32_t offset, uint32_t value, int size);
uint32_t read(uint32_t rgn_start, uint32_t offset, int size);
void write(uint32_t rgn_start, uint32_t offset, uint32_t value, int size);
// InterruptCtrl methods
uint32_t register_dev_int(IntSrc src_id);

View File

@ -40,7 +40,7 @@ HammerheadCtrl::HammerheadCtrl() : MemCtrlBase()
add_mmio_region(0xF8000000, 0x500, this);
}
uint32_t HammerheadCtrl::read(uint32_t reg_start, uint32_t offset, int size)
uint32_t HammerheadCtrl::read(uint32_t rgn_start, uint32_t offset, int size)
{
uint32_t result;

View File

@ -97,7 +97,7 @@ public:
}
// MMIODevice methods
uint32_t read(uint32_t reg_start, uint32_t offset, int size);
uint32_t read(uint32_t rgn_start, uint32_t offset, int size);
void write(uint32_t rgn_start, uint32_t offset, uint32_t value, int size);
void insert_ram_dimm(int slot_num, uint32_t capacity);

View File

@ -41,7 +41,7 @@ HMC::HMC() : MemCtrlBase()
this->bit_pos = 0;
}
uint32_t HMC::read(uint32_t reg_start, uint32_t offset, int size)
uint32_t HMC::read(uint32_t rgn_start, uint32_t offset, int size)
{
if (!offset)
return !!(this->ctrl_reg & (1ULL << this->bit_pos++));
@ -49,7 +49,7 @@ uint32_t HMC::read(uint32_t reg_start, uint32_t offset, int size)
return 0; /* FIXME: what should be returned for invalid offsets? */
}
void HMC::write(uint32_t reg_start, uint32_t offset, uint32_t value, int size)
void HMC::write(uint32_t rgn_start, uint32_t offset, uint32_t value, int size)
{
uint64_t bit;

View File

@ -51,8 +51,8 @@ public:
}
/* MMIODevice methods */
uint32_t read(uint32_t reg_start, uint32_t offset, int size);
void write(uint32_t reg_start, uint32_t offset, uint32_t value, int size);
uint32_t read(uint32_t rgn_start, uint32_t offset, int size);
void write(uint32_t rgn_start, uint32_t offset, uint32_t value, int size);
uint64_t get_control_reg(void) {
return this->ctrl_reg;

View File

@ -74,10 +74,10 @@ int MPC106::device_postinit()
return 0;
}
uint32_t MPC106::read(uint32_t reg_start, uint32_t offset, int size) {
uint32_t MPC106::read(uint32_t rgn_start, uint32_t offset, int size) {
uint32_t result;
if (reg_start == 0xFE000000) {
if (rgn_start == 0xFE000000) {
// broadcast I/O request to devices that support I/O space
// until a device returns true that means "request accepted"
for (auto& dev : this->io_space_devs) {
@ -98,8 +98,8 @@ uint32_t MPC106::read(uint32_t reg_start, uint32_t offset, int size) {
return 0;
}
void MPC106::write(uint32_t reg_start, uint32_t offset, uint32_t value, int size) {
if (reg_start == 0xFE000000) {
void MPC106::write(uint32_t rgn_start, uint32_t offset, uint32_t value, int size) {
if (rgn_start == 0xFE000000) {
// broadcast I/O request to devices that support I/O space
// until a device returns true that means "request accepted"
for (auto& dev : this->io_space_devs) {
@ -143,9 +143,10 @@ uint32_t MPC106::pci_read(uint32_t size) {
} else {
LOG_F(
ERROR,
"%s err: read attempt from non-existing PCI device %d",
this->name.c_str(),
dev_num);
"%s err: read attempt from non-existing PCI device %02x:%02x.%x @%02x.%c",
this->name.c_str(), bus_num, dev_num, fun_num, reg_offs,
size == 4 ? 'l' : size == 2 ? 'w' : size == 1 ? 'b' : '0' + size
);
return 0;
}
}
@ -178,9 +179,11 @@ void MPC106::pci_write(uint32_t value, uint32_t size) {
} else {
LOG_F(
ERROR,
"%s err: write attempt to non-existing PCI device %d",
this->name.c_str(),
dev_num);
"%s err: write attempt to non-existing PCI device %02x:%02x.%x @%02x.%c = %0*x",
this->name.c_str(), bus_num, dev_num, fun_num, reg_offs,
size == 4 ? 'l' : size == 2 ? 'w' : size == 1 ? 'b' : '0' + size,
size * 2, value
);
}
}
}

View File

@ -52,8 +52,8 @@ public:
return std::unique_ptr<MPC106>(new MPC106());
}
uint32_t read(uint32_t reg_start, uint32_t offset, int size);
void write(uint32_t reg_start, uint32_t offset, uint32_t value, int size);
uint32_t read(uint32_t rgn_start, uint32_t offset, int size);
void write(uint32_t rgn_start, uint32_t offset, uint32_t value, int size);
int device_postinit();

View File

@ -53,7 +53,7 @@ PlatinumCtrl::PlatinumCtrl() : MemCtrlBase()
this->display_id = std::unique_ptr<DisplayID> (new DisplayID());
}
uint32_t PlatinumCtrl::read(uint32_t reg_start, uint32_t offset, int size)
uint32_t PlatinumCtrl::read(uint32_t rgn_start, uint32_t offset, int size)
{
if (size != 4) {
LOG_F(WARNING, "Platinum: unsupported register access size %d!", size);

View File

@ -155,7 +155,7 @@ public:
}
/* MMIODevice methods */
uint32_t read(uint32_t reg_start, uint32_t offset, int size);
uint32_t read(uint32_t rgn_start, uint32_t offset, int size);
void write(uint32_t rgn_start, uint32_t offset, uint32_t value, int size);
void insert_ram_dimm(int slot_num, uint32_t capacity);

View File

@ -133,9 +133,9 @@ int CharIoStdin::rcv_char(uint8_t* c) {
#include <sys/select.h>
#include <unistd.h>
struct sigaction old_act_sigint, new_act_sigint;
struct sigaction old_act_sigterm, new_act_sigterm;
struct termios orig_termios;
static struct sigaction old_act_sigint, new_act_sigint;
static struct sigaction old_act_sigterm, new_act_sigterm;
static struct termios orig_termios;
void CharIoStdin::mysig_handler(int signum)
{
@ -340,7 +340,7 @@ bool CharIoSocket::rcv_char_available()
if (consecutivechars >= 15) {
consecutivechars++;
if (consecutivechars >= 400)
if (consecutivechars >= 800)
consecutivechars = 0;
return 0;
}
@ -378,14 +378,19 @@ bool CharIoSocket::rcv_char_available()
if (FD_ISSET(sockfd, &readfds)) {
uint8_t c;
int received = recv(sockfd, &c, 1, 0);
if (received == -1 && acceptfd != -1) {
LOG_F(INFO, "socket sock read err: %s", strerror(errno));
if (received == -1) {
if (acceptfd == -1) {
//LOG_F(INFO, "socket sock read (not accepted yet) err: %s", strerror(errno)); // this happens once before accept
}
else {
LOG_F(INFO, "socket sock read err: %s", strerror(errno)); // should never happen
}
}
else if (received == 1) {
LOG_F(INFO, "socket sock read '%c'", c);
LOG_F(INFO, "socket sock read '%c'", c); // should never happen
}
else {
LOG_F(INFO, "socket sock read %d", received);
LOG_F(INFO, "socket sock read %d", received); // should never happen
}
if (acceptfd == -1) {

View File

@ -99,7 +99,7 @@ uint8_t EsccController::read(uint8_t reg_offset)
case EsccReg::Port_A_Data:
return this->ch_a->receive_byte();
default:
LOG_F(9, "ESCC: reading from unimplemented register %d", reg_offset);
LOG_F(9, "ESCC: reading from unimplemented register 0x%x", reg_offset);
}
return result;
@ -121,7 +121,7 @@ void EsccController::write(uint8_t reg_offset, uint8_t value)
this->ch_a->send_byte(value);
break;
default:
LOG_F(9, "ESCC: writing 0x%X to unimplemented register %d", value, reg_offset);
LOG_F(9, "ESCC: writing 0x%X to unimplemented register 0x%x", value, reg_offset);
}
}

View File

@ -378,11 +378,11 @@ bool ATIRage::pci_io_write(uint32_t offset, uint32_t value, uint32_t size) {
}
uint32_t ATIRage::read(uint32_t reg_start, uint32_t offset, int size)
uint32_t ATIRage::read(uint32_t rgn_start, uint32_t offset, int size)
{
LOG_F(8, "Reading ATI Rage PCI memory: region=%X, offset=%X, size %d", reg_start, offset, size);
LOG_F(8, "Reading ATI Rage PCI memory: region=%X, offset=%X, size %d", rgn_start, offset, size);
if (reg_start < this->aperture_base || offset > APERTURE_SIZE) {
if (rgn_start < this->aperture_base || offset > APERTURE_SIZE) {
LOG_F(WARNING, "ATI Rage: attempt to read outside the aperture!");
return 0;
}
@ -410,11 +410,11 @@ uint32_t ATIRage::read(uint32_t reg_start, uint32_t offset, int size)
return 0;
}
void ATIRage::write(uint32_t reg_start, uint32_t offset, uint32_t value, int size)
void ATIRage::write(uint32_t rgn_start, uint32_t offset, uint32_t value, int size)
{
LOG_F(8, "Writing reg=%X, offset=%X, value=%X, size %d", reg_start, offset, value, size);
LOG_F(8, "Writing reg=%X, offset=%X, value=%X, size %d", rgn_start, offset, value, size);
if (reg_start < this->aperture_base || offset > APERTURE_SIZE) {
if (rgn_start < this->aperture_base || offset > APERTURE_SIZE) {
LOG_F(WARNING, "ATI Rage: attempt to write outside the aperture!");
return;
}

View File

@ -56,8 +56,8 @@ public:
}
/* MMIODevice methods */
uint32_t read(uint32_t reg_start, uint32_t offset, int size);
void write(uint32_t reg_start, uint32_t offset, uint32_t value, int size);
uint32_t read(uint32_t rgn_start, uint32_t offset, int size);
void write(uint32_t rgn_start, uint32_t offset, uint32_t value, int size);
/* PCI device methods */
bool supports_io_space(void) {

View File

@ -103,11 +103,11 @@ void ControlVideo::notify_bar_change(int bar_num)
}
}
uint32_t ControlVideo::read(uint32_t reg_start, uint32_t offset, int size)
uint32_t ControlVideo::read(uint32_t rgn_start, uint32_t offset, int size)
{
uint32_t result = 0;
if (reg_start == this->vram_base) {
if (rgn_start == this->vram_base) {
if (offset >= 0x800000) {
return read_mem_rev(&this->vram_ptr[offset - 0x800000], size);
} else {
@ -124,15 +124,15 @@ uint32_t ControlVideo::read(uint32_t reg_start, uint32_t offset, int size)
result = this->cur_mon_id << 6;
break;
default:
LOG_F(INFO, "read from 0x%08X:0x%08X", reg_start, offset);
LOG_F(INFO, "read from 0x%08X:0x%08X", rgn_start, offset);
}
return BYTESWAP_32(result);
}
void ControlVideo::write(uint32_t reg_start, uint32_t offset, uint32_t value, int size)
void ControlVideo::write(uint32_t rgn_start, uint32_t offset, uint32_t value, int size)
{
if (reg_start == this->vram_base) {
if (rgn_start == this->vram_base) {
if (offset >= 0x800000) {
write_mem_rev(&this->vram_ptr[offset - 0x800000], value, size);
} else {
@ -209,7 +209,7 @@ void ControlVideo::write(uint32_t reg_start, uint32_t offset, uint32_t value, in
this->int_enable = value;
break;
default:
LOG_F(INFO, "write 0x%08X to 0x%08X:0x%08X", value, reg_start, offset);
LOG_F(INFO, "write 0x%08X to 0x%08X:0x%08X", value, rgn_start, offset);
}
}

View File

@ -97,8 +97,8 @@ public:
}
// MMIODevice methods
uint32_t read(uint32_t reg_start, uint32_t offset, int size);
void write(uint32_t reg_start, uint32_t offset, uint32_t value, int size);
uint32_t read(uint32_t rgn_start, uint32_t offset, int size);
void write(uint32_t rgn_start, uint32_t offset, uint32_t value, int size);
protected:
void notify_bar_change(int bar_num);

View File

@ -109,10 +109,6 @@ int initialize_gossamer(std::string& id)
grackle_obj->add_mmio_region(
0xFF000004, 4096, dynamic_cast<MMIODevice*>(gMachineObj->get_comp_by_name("MachineID")));
// register the Heathrow I/O controller with the PCI host bridge
grackle_obj->pci_register_device(
16, dynamic_cast<PCIDevice*>(gMachineObj->get_comp_by_name("Heathrow")));
// allocate ROM region
if (!grackle_obj->add_rom_region(0xFFC00000, 0x400000)) {
LOG_F(ERROR, "Could not allocate ROM region!");
@ -124,15 +120,11 @@ int initialize_gossamer(std::string& id)
setup_ram_slot("RAM_DIMM_2", 0x56, GET_INT_PROP("rambank2_size"));
setup_ram_slot("RAM_DIMM_3", 0x55, GET_INT_PROP("rambank3_size"));
// select built-in GPU name
std::string gpu_name = "AtiRageGT";
if (id == "pmg3twr") {
gpu_name = "AtiRagePro";
}
// register built-in ATI Rage GPU with the PCI host bridge
// add pci devices
grackle_obj->pci_register_device(
18, dynamic_cast<PCIDevice*>(gMachineObj->get_comp_by_name(gpu_name)));
16, dynamic_cast<PCIDevice*>(gMachineObj->get_comp_by_name("Heathrow")));
grackle_obj->pci_register_device(
18, dynamic_cast<PCIDevice*>(gMachineObj->get_comp_by_name(id == "pmg3twr" ? "AtiRagePro" : "AtiRageGT")));
// add Athens clock generator device and register it with the I2C host
gMachineObj->add_device("Athens", std::unique_ptr<AthensClocks>(new AthensClocks(0x28)));

View File

@ -1,5 +1,5 @@
The Apple Desktop Bus is a bit-serial peripheral bus, Apple themselves cited a 2-MHz Motorola 68HC11 microcontroller as an example platform to implement the ADB standard with. Its transfer speed is usually around 10.0 kilobits per second, roughly comparable to a PS/2 port at 12.0 kilobits per second.
The device commands are in the form of single byte strings. The first four bits are to signal which of the 16 devices are to be used. The next two bits are for which action to execute (talk, listen, flush, or reset). These are followed by two bits which determine the register to reference (register 0 is usually a communications register, while register 3 is used for device info).
# Commands

View File

@ -5,7 +5,7 @@ The ATI Rage is a video card that comes bundled with early Power Mac G3s and New
The ATI Rage can usually be located at IOBase (ex.: 0xF3000000 for Power Mac G3 Beige) + 0x9000. However, the video memory appears to be at 0x81000000 and is capped at 8 MB.
# Register Map
| Register Name | Offset |
|:-------------------:|:------:|
| BUS_CNTL | 0xA0 |

View File

@ -19,7 +19,7 @@ All registers are 32-bit here.
| Register | Offset |
|:-----------------:|:------:|
| Sound Control | 0x0 |
| Sound Control | 0x0 |
| Codec Control | 0x10 |
| Codec Status | 0x20 |
| Clipping Count | 0x30 |

View File

@ -1,5 +1,5 @@
The BMac is an Ethernet controller featured in G3 and early G4 Macs. As described by a Linux contributor, this controller "appears to have some parts in common with the Sun "Happy Meal" (HME) controller".
The max frame size is 0x5EE bytes.
It resides on 0xF3011000, with Writing DMA on 0xF3008200 and Reading DMA on 0xF3008300.

View File

@ -0,0 +1,306 @@
get-inherited-property and map-in notes
by joevt
==========================================================================================
PowerMac 8600
rom notes:
The Open Firmware part (192k) of the 4MB ROM in the 8600 is the same in the 9500 and is probably the same for these other Macs:
" AAPL,7500"
" AAPL,8500"
" AAPL,9500"
" AAPL,7300"
Detokenized FCode from Mac ROM:
external-token get-inherited-property \ [0x0ca] 0x21d
b(:) \ [0x0b7]
2 mac_rom_401 \ mac_rom_code_45C \ [0x401] \ I don't know how token 401 turns into token 45C or the how the other tokens change.
?my-self \ [0x654]
8 mac_rom_table_2 \ mac_rom_code_45E \ [0x41a] \ The fcode compiler is probably responsible for these changes.
b(<mark) \ [0x0b1]
8 mac_rom_table_1 \ mac_rom_code_45D \ [0x412]
b?branch \ [0x014] 0x30
8 mac_rom_table_1 \ mac_rom_code_45D \ [0x412]
mac_rom_field_512_4 \ [0x512]
@ \ [0x06d]
0xC mac_rom_table_2 \ mac_rom_code_45E \ [0x41b]
0 mac_rom_table_1 \ mac_rom_code_45D \ [0x410]
4 mac_rom_table_1 \ mac_rom_code_45D \ [0x411]
0xC mac_rom_table_1 \ mac_rom_code_45D \ [0x413]
unnamed_fcode_79e \ [0x79e]
?dup \ [0x050]
b?branch \ [0x014] 0x14
dup \ [0x047]
unnamed_fcode_796 \ [0x796]
@ \ [0x06d]
swap \ [0x049]
unnamed_fcode_797 \ [0x797]
@ \ [0x06d]
\\ missing dup here!!!!
-1 \ [0x0a4]
= \ [0x03c]
b?branch \ [0x014] 0x4
drop \ [0x046]
execute \ [0x01d]
b(>resolve) \ [0x0b2]
false \ [0x557]
exit \ [0x033]
b(>resolve) \ mac_rom_code_45D \ [0x0b2]
8 mac_rom_table_1 \ [0x412]
mac_rom_field_513_8 \ [0x513]
@ \ mac_rom_code_45E \ [0x06d]
8 mac_rom_table_2 \ [0x41a]
bbranch \ [0x013] 0xffffffcf
b(>resolve) \ [0x0b2]
true \ [0x558]
b(;) \ [0x0c2]
Detokenized FCode converted to Forth:
: get-inherited-property \ (21d) [0ca 0b7]
2 mac_rom_401 \ [401]
?my-self ( -- ihandle ) \ return iHandle of current instance \ [654]
8 mac_rom_table_2 \ [41a]
begin \ [0b1]
8 mac_rom_table_1 \ [412]
while \ (0x30) [014]
8 mac_rom_table_1 \ [412]
mac_rom_field_512_4 \ [512]
@ \ [06d]
0xC mac_rom_table_2 \ [41b]
0 mac_rom_table_1 \ [410]
4 mac_rom_table_1 \ [411]
0xC mac_rom_table_1 \ [413]
colon_definition_function_79e \ (79e) [79e]
?dup \ [050]
if \ (0x14) [014]
dup \ [047]
field_796 \ (796) [796]
@ \ [06d]
swap \ [049]
field_797 \ (797) [797]
@ \ [06d]
\\ missing dup here!!!!
-1 \ [0a4]
= \ [03c]
if \ (0x4) [014]
drop \ [046]
execute \ [01d]
then \ [0b2]
false \ [557]
exit \ [033]
then \ [0b2]
8 mac_rom_table_1 \ [412]
mac_rom_field_513_8 \ [513]
@ \ [06d]
8 mac_rom_table_2 \ [41a]
repeat \ (0xffffffcf) [013 0b2]
true \ [558]
; \ [0c2]
Disassembled PPC compiled FCode taken from Open Firmware RAM:
Offset Hex Dest Offset Instruction Operands Comments
0143C0: ........ \ fcode link (offset) to previous word or 00000000 for first word
0143C4: 80 \ fcode flags 80 = fdefd (fcode is defined)
0143C5: b(:) \ [0x0b7] 0x21d get-inherited-property
0143E0: 967E FFFC stwu r19,-4(r30) \ execution token is here - always a multiple of 8
0143E4: 7E68 02A6 mflr r19 \ every compiled b(:) word starts with these two lines 967EFFFC, 7E6802A6
0143E8: 3860 0002 li r3,2
0143EC: 4BFE DA1D 001E08 bl mac_rom_code_45C
0143F0: 4BFF 5EA9 00A298 bl ?my-self
0143F4: 3860 0008 li r3,8
0143F8: 4BFE DA69 001E60 bl mac_rom_code_45E
0143FC: 3860 FFFF li r3,-1
014400: 3860 0008 li r3,8
014404: 4BFE DA45 001E48 bl mac_rom_code_45D
014408: 4BFE D871 001C78 bl mac_rom_code_455
01440C: 4800 0098 0144A4 b $+152
014410: 3860 0008 li r3,8
014414: 4BFE DA35 001E48 bl mac_rom_code_45D
014418: 4BFF 0551 004968 bl mac_rom_field_512_4
01441C: 4BFE E52D 002948 bl @
014420: 3860 000C li r3,0x000C
014424: 4BFE DA3D 001E60 bl mac_rom_code_45E
014428: 3860 0000 li r3,0
01442C: 4BFE DA1D 001E48 bl mac_rom_code_45D
014430: 3860 0004 li r3,4
014434: 4BFE DA15 001E48 bl mac_rom_code_45D
014438: 3860 000C li r3,0x000C
01443C: 4BFE DA0D 001E48 bl mac_rom_code_45D
014440: 4BFF F321 013760 bl mac_rom_colon_79E
014444: 4BFE E965 002DA8 bl ?dup
014448: 4BFE D831 001C78 bl mac_rom_code_455
01444C: 4800 003C 014488 b $+60
014450: 4BFE E921 002D70 bl dup
014454: 4BFF F1FD 013650 bl mac_rom_field_796
014458: 4BFE E4F1 002948 bl @
01445C: 4BFE EA95 002EF0 bl swap
014460: 4BFF F209 013668 bl mac_rom_field_797
014464: 4BFE E4E5 002948 bl @
014468: 4BFE EBD9 003040 bl -1
01446C: 4BFE F425 003890 bl =
014470: 4BFE D809 001C78 bl mac_rom_code_455
014474: 4800 000C 014480 b $+12
014478: 4BFE E999 002E10 bl drop
01447C: 4BFE CBE5 001060 bl execute
014480: 4BFF 2681 006B00 bl false
014484: 4BFE CC15 001098 bl exit
014488: 3860 0008 li r3,8
01448C: 4BFE D9BD 001E48 bl mac_rom_code_45D
014490: 4BFF 04E9 004978 bl mac_rom_field_513_8
014494: 4BFE E4B5 002948 bl @
014498: 3860 0008 li r3,8
01449C: 4BFE D9C5 001E60 bl mac_rom_code_45E
0144A0: 4BFF FF60 014400 b $-160
0144A4: 4BFF 267D 006B20 bl true
0144A8: 4BFE CBF0 001098 b exit \ every compiled b(:) word starts ends with a branch to exit
0144AC: 0000 0000 \ filler makes fcode length a multiple of 8
========================================
The following Forth script changes the 0x22nd instruction (offset 0x88) in get-inherited-property from the Forth word "-1" to "dup -1".
: myGIPpatch dup -1 ;
' get-inherited-property 22 na+ ' myGIPpatch BLpatch
The following tests the patch:
" crap" get-inherited-property \ should return true
" name" get-inherited-property \ should return nameaddr, namelen, false
The following undoes the patch:
' get-inherited-property 22 na+ ' -1 BLpatch
I don't know what kind of properties would return a length of -1 which would cause the drop and execute words to run but there would be no address to execute (stack underflow) without the dup word in the patch.
A method needs to be used to decide if the patch is needed:
- test for add-range (see below)
- test get-inherited-property and check if stack depth is 2 instead of 3
The patch offset (0x88) will have to be verified for every Mac that has the bug.
=========
The Radeon 7000 defines an unnamed value that can have one of following values:
0: for machines that do not have the "AAPL,cpu-id" property (newer Macs like the iMac)
1: for machines that have the "AAPL,cpu-id" property but do not have the get-inherited-property bug
3: for machines that have the "AAPL,cpu-id" property and the get-inherited-property bug (older Macs like the 8600)
However, when using the value, the Radeon 7000 only distinguishes between 0 and not 0 (doesn't have "AAPL,cpu-id" and has "AAPL,cpu-id"). If the value is not zero, the Radeon 7000 uses "assigned-addresses" in the calculation of the address for the map-in method.
The method that the Radeon 7000 uses (the "AAPL,cpu-id" check) to decide whether or not assigned-addresses needs to be used is different than the method described by the "PCI Bus Binding to Open Firmware Revision 2.1" documentation. I don't know why ATI didn't do it that way.
==========================================================================================
PowerMac G3 233 (beige) Open Firmware 2.0f1:
- The PCI bridge in the G3 does not implement the add-range method which signifies that get-inherited-property returns the correct number of parameters
- get-inherited-property is implemented the same as Open Firmware 2.4 (see below)
==========================================================================================
PowerMac G3 300 (beige) Open Firmware 2.4:
- this version of Open Firmware is the best Old World Mac Open Firmware to work with since it has names for almost everything.
Detokenized FCode converted to Forth:
: get-inherited-property \ (21d) [0ca 0b7]
{ local_0 local_1 ; local_2 local_3 } \ [409]
?my-self \ [652]
-> local_2 \ [41a]
begin \ [0b1]
local_2 \ [412]
while \ (0x31) [014]
local_2 \ [412]
>in.device-node \ [526]
@ \ [06d]
-> local_3 \ [41b]
local_0 \ [410]
local_1 \ [411]
local_3 \ [413]
find-property \ [790]
?dup \ [050]
if \ (0x15) [014]
dup \ [047]
>prop.addr \ [788]
@ \ [06d]
swap \ [049]
>prop.len \ [789]
@ \ [06d]
dup \ [047]
-1 \ [0a4]
= \ [03c]
if \ (0x4) [014]
drop \ [046]
execute \ [01d]
then \ [0b2]
false \ [56b]
exit \ [033]
then \ [0b2]
local_2 \ [412]
>in.my-parent \ [527]
@ \ [06d]
-> local_2 \ [41a]
repeat \ (0xffce) [013 0b2]
true \ [56c]
; \ [0c2]
==========================================================================================
from "Writing FCode Programs For PCI"
\ Some of Apples Open Firmware implementations have a bug in their map-in method. The
\ bug causes phys.lo and phys.mid to be treated as absolute addresses rather than
\ offsets even when working with relocatable addresses.
\ To overcome this bug, the Open Firmware Working Group in conjunction with Apple has
\ adopted a workaround that is keyed to the presence or absence of the add-range method
\ in the PCI node. If the add-range method is present in an Apple ROM, the map-in
\ method is broken. If the add-range property is absent, the map-in method behaves
\ correctly.
\ The following methods allow the FCode driver to accomodate both broken and working
\ map-in methods.
: map-in-broken? ( -- flag )
\ Look for the method that is present when the bug is present
" add-range" my-parent ihandle>phandle ( adr len phandle )
find-method dup if nip then ( flag ) \ Discard xt if present
;
==========================================================================================
from "PCI Bus Binding to Open Firmware Revision 2.1"
1. The method add-range is reserved and shall not be implemented in future releases of OFW. The
presence of this method in the PCI node indicates to child nodes both that the map-in method of this
bus node requires that the phys.lo address to be extracted from the "assigned-addresses" prop-erty
and that the get-inherited-property method does not return the prop-len (only the
prop-addr and false) if the property is found. The non-existence of this method indicates to child
nodes that the phys.lo address is an offset relative to the base address (when n=0) and that get-inher-ited-
property returns three stack items (prop-addr prop-len and false) if the inherited
property is found.
==========================================================================================

View File

@ -22,3 +22,21 @@ Device ID: 0x0002 (MPC106)
Within the Mac's own device tree, this is usually device 0.
It also spans for 0x7F000000 bytes starting from 0x80000000.
# Adding PCI devices to dingusppc
The dingusppc CLI can add known PCI devices to a PCI slot (A1, B1, C1).
Only the following device numbers are probed by Open Firmware:
- @0 used by pci [grackle]
- @c slot PERCH interrupt 0x1c
- @d slot A1 interrupt 0x17
- @e slot B1 interrupt 0x18
- @f slot C1 interrupt 0x19
- @10 used by mac-io [heathrow] which includes many devices with different interrupts
- @12 slot F1 interrupt 0x16 used by AtiRageGT or AtiRagePro
With minor additions to source code, dingusppc can add a known PCI device to any device number between @1 and @1f except for @10 and @12.
A nvramrc patch can make Open Firmware probe the other device numbers. An OS might be able to probe these other devices numbers even if they are not probed by Open Firmware.

View File

@ -8,7 +8,7 @@ computers. As those names suggest, Apple engineers liked to name their I/O contr
after airports and train stations.
Heathrow and its siblings are collectively referred to as __mac-io__ devices in the
OpenFirmware device tree.
Open Firmware device tree.
## Mac I/O family
@ -134,7 +134,7 @@ Macintosh firmware configures the Heathrow ASIC to live at address `0xF3000000`.
#### Feature control register
Bit names in the table below were pulled from OpenFirmware v2.4.
Bit names in the table below were pulled from Open Firmware v2.4.
The field "Description" represents my personal attempt to describe the function
of those bits based on publicly available Apple and Linux sources.

View File

@ -9,7 +9,7 @@
| 0x68080000 | Opcode Dispatch Table |
| 0x68FFE000 | KernelData |
| 0x68FFF000 | EmulatorData |
| 0xFF800000 | OpenFirmware |
| 0xFF800000 | Open Firmware |
| 0xFFF0C000 | HardwarePriv |
# PHYSICAL MEMORY MAP
@ -44,14 +44,14 @@
### Main Memory
* 0x00000000 - 0x7FFFFFFF
Mac OS
Mac OS
* 0x00400000 - Open Firmware
* 0x00400000 - OpenFirmware
### PCI/Device Memory Area 0x80000000 - 0xFF000000
* 0x81000000 - Video Display Device (normally)
* 0xF3000000 -
Mac OS I/O Device area

View File

@ -2,7 +2,7 @@
| Register Name | Number |
|:----------------:|:------:|
| R_COUNT0 | 0x0 |
| R_COUNT0 | 0x0 |
| R_COUNT1 | 0x1 |
| R_FIFO | 0x2 |
| R_CMD | 0x3 |
@ -23,7 +23,7 @@
| Command Name | Number |
|:----------------:|:------:|
| NOP | 0x0 |
| NOP | 0x0 |
| ARBITRATE | 0x1 |
| SELECT | 0x2 |
| COMMAND | 0x3 |

View File

@ -12,7 +12,7 @@ The Description-Based Direct Memory Access relies on memory-based descriptions,
| Channel | Number |
|:-----------------:|:------:|
| SCSI0 | 0x0 |
| SCSI0 | 0x0 |
| FLOPPY | 0x1 |
| ETHERNET TRANSMIT | 0x2 |
| ETHERNET RECIEVE | 0x3 |
@ -27,9 +27,9 @@ The Description-Based Direct Memory Access relies on memory-based descriptions,
# NCR 53C94
The NCR 53C94 is the SCSI controller.
# Register Map
| Offset | Read functionality |Write functionality |
|:------:|:------------------------:|:-------------------------:|
| 0x0 | Transfer counter LSB | Transfer counter LSB |
@ -49,7 +49,7 @@ The NCR 53C94 is the SCSI controller.
# SWIM 3
The SWIM 3 (Sanders-Wozniak integrated machine 3) is the floppy drive disk controller. As can be inferred by the name, the SWIM III chip is the improvement of a combination of floppy disk driver designs by Steve Wozniak (who worked on his own floppy drive controller for early Apple computers) and Wendell B. Sander (who worked on an MFM-compatible IBM floppy drive controller).
The SWIM 3 (Sanders-Wozniak integrated machine 3) is the floppy drive disk controller. As can be inferred by the name, the SWIM III chip is the improvement of a combination of floppy disk driver designs by Steve Wozniak (who worked on his own floppy drive controller for early Apple computers) and Wendell B. Sander (who worked on an MFM-compatible IBM floppy drive controller).
The SWIM chip is resided on the logic board physically and is located at IOBase + 0x15000 in the device tree. It sits between the I/O controller and the floppy disk connector. Its function is to translate the I/O commands to specialized signals to drive the floppy disk drive, i.e. disk spinning speed, head position, phase sync, etc.
@ -79,4 +79,4 @@ Mac OS relies on 8 KB of NVRAM at minimum to run properly. It's usually found at
# Miscellaneous
The Power Mac G3 Beige has an additional register at 0xFF000004, which is dubbed varyingly as the "cpu-id" (by OpenFirmware), the ""systemReg" (display driver) or "MachineID" (platform driver).
The Power Mac G3 Beige has an additional register at 0xFF000004, which is dubbed varyingly as the "cpu-id" (by Open Firmware), the ""systemReg" (display driver) or "MachineID" (platform driver).

View File

@ -1,30 +1,30 @@
# OpenFirmware in Power Macintosh
# Open Firmware in Power Macintosh
*compiled from various sources by Max Poliakovski.*
[Open Firmware](https://en.wikipedia.org/wiki/Open_Firmware) is a platform-independent
boot firmware architecture covered by an IEEE standard.
All PowerMacintosh computers run OpenFirmware except the very first generation
All PowerMacintosh computers run Open Firmware except the very first generation
that uses [Nubus](https://en.wikipedia.org/wiki/NuBus) instead of
[PCI](https://en.wikipedia.org/wiki/Peripheral_Component_Interconnect).
OpenFirmware is used to perform hardware identification and initialization during
Open Firmware is used to perform hardware identification and initialization during
the booting process after power-on. It also provides a platform-independent
description of the attached devices available for operating systems.
In this respect, OpenFirmware can be compared with [BIOS](https://en.wikipedia.org/wiki/BIOS),
In this respect, Open Firmware can be compared with [BIOS](https://en.wikipedia.org/wiki/BIOS),
widely used in the PC world.
Being based upon the [Forth programming language](https://en.wikipedia.org/wiki/Forth_(programming_language)),
OpenFirmware offers an operating system, an interactive environment as well as a
Open Firmware offers an operating system, an interactive environment as well as a
programming language in one package. Its shell can be used as well by users for
controlling the boot enviroment as by developers for developing and debugging device
drivers.
This document focuses on various aspects of Apple's OpenFirmware implementation
This document focuses on various aspects of Apple's Open Firmware implementation
as found in various PowerMacintosh models.
## OpenFirmware Versions
## Open Firmware Versions
### Old World Macs
@ -43,17 +43,17 @@ as found in various PowerMacintosh models.
*TBD*
## OpenFirmware image
## Open Firmware image
### Old World Macs
OpenFirmware in OldWorld Macs is stored in the monolithic 4MB ROM. Its hibernated
Open Firmware in OldWorld Macs is stored in the monolithic 4MB ROM. Its hibernated
image is located at offset `0x320000` or `0x330000` from beginning of the ROM.
That corresponds to the physical address `0xFFF20000` or `0xFFF30000`, respectively.
The size of the OpenFirmware image varies from 98KB (v1.0.5) to 172KB (v2.4).
The size of the Open Firmware image varies from 98KB (v1.0.5) to 172KB (v2.4).
Apple's OpenFirmware image has the following structure:
Apple's Open Firmware image has the following structure:
| Section type | Architecture | Relative Size (v1.0.5) | Relative Size (v2.4) |
|:------------------:|:------------:|:----------------------:|:--------------------:|
@ -112,9 +112,119 @@ FFF20084 dc.l 0x7720 ; offset to the last Forth word descriptor of the kernel
```
### OpenFirmware internals
This StartVec structure in ROM is not to be confused with the `@startvec` structure in RAM.
Open Firmware 2.4 lists the fields of `@startvec`:
Apple's OpenFirmware contains a small kernel implemented in the native PowerPC code. This kernel performs the following actions:
```
0x488 0000 FF808000 >of.next 00001000
0x489 0004 FF808004 >of.dev-ven 00000000
0x48A 0008 FF808008 >of.class 00000000
0x48B 000C FF80800C >of.offset 00000000
0x48C 0010 FF808010 >of.fcode 00000000
0x48D 0008 FF808008 >imagesize 00000000
0x48E 000C FF80800C >'cold2 00000000
0x48F 0010 FF808010 >'map-page 00000000
0x490 0014 FF808014 >'map-io 00000000
0x491 0018 FF808018 >endiango FF808940
0x492 001C FF80801C >little? 00000000
0x493 0020 FF808020 >swizzle? 00000000
0x494 0024 FF808024 >real? 00000000
0x495 0028 FF808028 >rom-base FFF2003C
0x496 002C FF80802C >real_base 00400000
0x497 0030 FF808030 >virt_base FF800000
0x498 0034 FF808034 >real-vt-hd 004DFC00
0x499 0038 FF808038 >restart FFF201DC
0x49A 003C FF80803C >fcimage FFF27954
0x49B 0040 FF808040 >fcfiles FFF48C84
0x49C 0044 FF808044 >lasttoken 00000EB5
0x49D 0048 FF808048 >word-list FF85D4E8
0x49E 004C FF80804C >'ferror FF80D0B0 ferror
0x49F 0050 FF808050 >'(poplocals) FF80A350 (poplocals)
0x4A0 0054 FF808054 >'cold-load FF80F738 cold-load
0x4A1 0058 FF808058 >'cold-init FF818350 _cold-init
0x4A2 005C FF80805C >'quit FF8179A8 quit
0x4A3 0060 FF808060 >'abort FF811FA0 abort
0x4A4 0064 FF808064 >'syscatch FF819638 _syscatch
0x4A5 0068 FF808068 >'excp FF819338 _exception
0x4A6 006C FF80806C >'bp-done 00000000
0x4A7 0070 FF808070 >'step-done 00000000
0x4A8 0074 FF808074 >'mini-nub 00000000
0x4A9 0078 FF808078 >hwinitlr FFF04D50
0x4AA 007C FF80807C >hwinitiv 00000000
0x4AB 0080 FF808080 >hwinit3 00009000
0x4AC 0084 FF808084 >hwinit4 00009120
0x4AD 0088 FF808088 >hwinit8 FFF03058
0x4AE 008C FF80808C >hwinit9 F3060000
0x4AF 0090 FF808090 >sccac F3013020
0x4B0 0094 FF808094 >sccad F3013030
0x4B1 0098 FF808098 >ramsize 30000000
0x4B2 009C FF80809C >cpuclock 0DF092B0
0x4B3 00A0 FF8080A0 >busclock 03FB97A0
0x4B4 00A4 FF8080A4 >tbclock 00FEE5E8
0x4B5 00A8 FF8080A8 >here FF85D524
0x4B6 00AC FF8080AC >free-top FF8E0000
0x4B7 00B0 FF8080B0 >free-bot FF8D0000
0x4B8 00B4 FF8080B4 >#dsi-ints 00000000
0x4B9 00B8 FF8080B8 >#isi-ints 00000000
0x4BA 00BC FF8080BC >dl-buf FF8F0800
0x4BB 00C0 FF8080C0 >ttp800 FF8D6000
0x4BC 00C4 FF8080C4 >'cientry FF809E18 cientry
0x4BD 00C8 FF8080C8 >'cicall FF829498 cicall
0x4BE 00CC FF8080CC >'my-self FF80D8D0
0x4BF 00D0 FF8080D0 >'<sc-int> 00409D10
0x4C0 00D4 FF8080D4 >ofregsv FF8DFA00 ^-720600
0x4C1 00D8 FF8080D8 >ciregsv FF8DF800
0x4C2 00DC FF8080DC >ofregsr 004DFA00
0x4C3 00E0 FF8080E0 >ciregsr 004DF800
0x4C4 00E4 FF8080E4 >intvectv FF8DF600
0x4C5 00E8 FF8080E8 >intvectr 004DF600
0x4C6 00EC FF8080EC >regsvalid? FF808000
0x4C7 00F0 FF8080F0 >ciwords FF82A3D0
0x4C8 00F4 FF8080F4 >htab 004E0000
0x4C9 00F8 FF8080F8 >keepbat2? 00000000
0x4CA 00FC FF8080FC >keepbat3? 00000000
0x4CB 0100 FF808100 >rp FF800800
0x4CC 0104 FF808104 >dp FF800400
0x4CD 0108 FF808108 >fp FF801000
0x4CE 010C FF80810C >lp FF800C00
0x4CF 0110 FF808110 >ep FF801800
0x4D0 0114 FF808114 >ttp FF804000
0x4D1 0118 FF808118 >tib FF801C00
0x4D2 011C FF80811C >noname FF802000
0x4D3 0120 FF808120 >dec-ints 00000000
0x4D4 0124 FF808124 >dec-msec 00004141
0x4D5 0128 FF808128 >r13-31 FFF03078
```
The above is produced by this:
```
: dump_fields { addr fcodestart fcodeend ; token }
cr fcodeend 1+ fcodestart do
i ." 0x" 3 u.r space
i get-token drop -> token
0 token execute 4 u.r space
addr token execute dup 8 u.r space
token name.
#out @ d# 42 < if d# 42 #out @ - spaces then
@ dup 8 u.r space
\ check if the field might point to a xt token and if so, output its name
dup @startvec u> if
dup dup xt>hdr 4+ dup c@ + 8 + -8 and = if name. else drop then
else drop then
cr
loop
;
: dumpstartvec
@startvec 488 4d5 dump_fields ;
dumpstartvec
```
### Open Firmware internals
Apple's Open Firmware contains a small kernel implemented in the native PowerPC code. This kernel performs the following actions:
* set up memory translation for OF execution
* relocate itself from ROM to RAM
@ -123,26 +233,30 @@ Apple's OpenFirmware contains a small kernel implemented in the native PowerPC c
* pass control to recompiled OF that starts building the device tree
* process low-level exceptions
## OpenFirmware and the Macintosh boot process
## Open Firmware and the Macintosh boot process
### Old World Macs
1. In response to power coming on, HWInit code in the Power Macintosh ROM performs initialization of the memory controller and the basic I/O facilities as well as some self-testing. After that, the startup chime is played.
2. HWInit passes control to OpenFirmware kernel that prepares OF execution from RAM. OF builds the **device tree** - a platform-independent description of the attached HW.
2. HWInit passes control to Open Firmware kernel that prepares OF execution from RAM. OF builds the **device tree** - a platform-independent description of the attached HW.
3. OF returns control to HWInit that initializes several low-level data structures required by the Nanokernel.
4. HWInit passes control to the Nanokernel that initializes the native execution enviroment and the 68k emulator.
5. 68k emulator executes the start-up code in the Macintosh ROM that initializes various managers.
6. The device tree generated by the OpenFirmware in step 2 is imported by the Expansion Bus Manager initialization code and stored in the **NameRegistry**.
6. The device tree generated by the Open Firmware in step 2 is imported by the Expansion Bus Manager initialization code and stored in the **NameRegistry**.
7. An operating system is located and loaded.
### New World Macs
*TBD*
## OpenFirmware bugs
## Open Firmware bugs
Apple OF is known to contain numerous bugs. The following table lists some recently discrovered bugs, not mentioned elsewhere.
| OF version affected | Bug description |
|:-------------------:|-----------------|
| 2.0f1, 2.4 | A numerical overflow in `um/mod` used by `get-usecs-60x` causes the OF console to become unresponsive after approx. 71 minutes. You have to restart your computer once the bug is triggered. |
| 1.0.5 | /chaos/control `fill-rectangle` is ( ? color y x w h -- ) instead of ( color x y w h -- ) |
| 2.4 | string literal buffers `"` on the command line are 256 bytes but will overflow if you enter more than 256 bytes. |
`get-inherited-property notes.txt` discusses known bugs regarding get-inherited-property and map-in in Open Firmware 1.0.5.

View File

@ -8,7 +8,7 @@ Code execution generally begins at 0xFFF00100, which the reset exception vector.
# BATs
The 601 BATs are emulated by the OpenFirmware.
The 601 BATs are emulated by the Open Firmware.
# TLBs