Skip to content

Fix lockup involving specific mount order #2310

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Mar 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/windows-msvc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ jobs:
max_warnings: 20
- name: MSVC 64-bit
arch: x64
max_warnings: 910
max_warnings: 904
steps:
- name: Checkout repository
uses: actions/checkout@v3
Expand Down
10 changes: 10 additions & 0 deletions include/dos_system.h
Original file line number Diff line number Diff line change
Expand Up @@ -380,6 +380,16 @@ void DOS_AddDevice(DOS_Device * adddev);
/* DelDevice destroys the device that is pointed to. */
void DOS_DelDevice(DOS_Device * dev);

// Get, append, and query the DOS device header linked list
RealPt DOS_GetNextDevice(const RealPt rp);
RealPt DOS_GetLastDevice();
void DOS_AppendDevice(const uint16_t segment, const uint16_t offset = 0);
bool DOS_IsLastDevice(const RealPt rp);
bool DOS_DeviceHasName(const RealPt rp, const std::string_view req_name);
bool DOS_DeviceHasAttributes(const RealPt rp, const uint16_t attributes);
uint16_t DOS_GetDeviceStrategy(const RealPt rp);
uint16_t DOS_GetDeviceInterrupt(const RealPt rp);

void VFILE_Register(const char *name,
const uint8_t *data,
const uint32_t size,
Expand Down
198 changes: 154 additions & 44 deletions src/dos/dos_devices.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -191,54 +191,64 @@ uint8_t DOS_ExtDevice::GetStatus(bool input_flag)
return 0x00;
}

uint32_t DOS_CheckExtDevice(const char *name, bool already_flag)
// Walk the DOS devices and return the real pointer to the device matching the
// given name, provided it's not an existing driver (if indicated).
RealPt DOS_CheckExtDevice(const std::string_view name, const bool skip_existing_drivers)
{
uint32_t addr = dos_infoblock.GetDeviceChain();
uint16_t seg, off;
uint16_t next_seg, next_off;
uint16_t no;
char devname[8 + 1];

seg = addr >> 16;
off = addr & 0xffff;
while (1) {
no = real_readw(seg, off + 4);
next_seg = real_readw(seg, off + 2);
next_off = real_readw(seg, off);
if (next_seg == 0xffff && next_off == 0xffff) {
break;
}
if (no & 0x8000) {
for (no = 0; no < 8; no++) {
if ((devname[no] = real_readb(seg, off + 10 + no)) <= 0x20) {
devname[no] = 0;
break;
}
// Helper lambda's to check various device properties
//
auto is_a_driver = [](const RealPt rp) {
constexpr uint16_t driver_flag = (1 << 15);
return DOS_DeviceHasAttributes(rp, driver_flag);
};

auto is_con_or_nul = [](const RealPt rp) {
constexpr uint16_t con_strategy = 0;
constexpr uint16_t con_interrupt = 0;

constexpr uint16_t nul_strategy = 0xffff;
constexpr uint16_t nul_interrupt = 0xffff;

const auto strategy = DOS_GetDeviceStrategy(rp);
const auto interrupt = DOS_GetDeviceInterrupt(rp);

return (strategy == con_strategy && interrupt == con_interrupt) ||
(strategy == nul_strategy && interrupt == nul_interrupt);
};

auto is_existing_driver = [](const RealPt rp) {
for (const auto& dev : Devices) {
if (!dev || (dev->GetInformation() & EXT_DEVICE_BIT) == 0) {
continue;
}
devname[8] = 0;
if (!strcmp(name, devname)) {
if (already_flag) {
for (no = 0; no < DOS_DEVICES; no++) {
if (Devices[no]) {
if (Devices[no]->GetInformation() & EXT_DEVICE_BIT) {
if (((DOS_ExtDevice *)Devices[no])
->CheckSameDevice(seg, real_readw(seg, off + 6),
real_readw(seg, off + 8))) {
return 0;
}
}
}
}
}
// Exclude the default CON and NUL
if (real_readd(seg, off + 6) == 0 || real_readd(seg, off + 6) == 0xffffffff) {
return 0;
}
return (uint32_t)seg << 16 | (uint32_t)off;
const auto ext_dev = dynamic_cast<DOS_ExtDevice*>(dev);
if (ext_dev->CheckSameDevice(RealSeg(rp),
DOS_GetDeviceStrategy(rp),
DOS_GetDeviceInterrupt(rp))) {
return true;
}
}
seg = next_seg;
off = next_off;
return false;
};

// start walking the device chain of real pointers
auto rp = dos_infoblock.GetDeviceChain();

while (!DOS_IsLastDevice(rp)) {
if (!is_a_driver(rp) || !DOS_DeviceHasName(rp, name)) {
rp = DOS_GetNextDevice(rp);
continue;
}
if (is_con_or_nul(rp)) {
return 0;
}
if (skip_existing_drivers && is_existing_driver(rp)) {
return 0;
}
// The device at the real pointer is a driver, has a name
// matching the given name, is neither the CON nor NUL devices,
// and (if requested) is not an existing device driver.
return rp;
}
return 0;
}
Expand Down Expand Up @@ -420,6 +430,106 @@ void DOS_DelDevice(DOS_Device * dev) {
}
}

// Structure of internal DOS tables, Device Driver
//
// Property Offset Type Description
// ~~~~~~~~ ~~~~~~ ~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// next_rpt 00h DWord --> Next driver in chain (x:FFFF means end)
// attributes 04h Word Device attributes.
// strategy 06h Word Device strategy routine offset.
// interrupt 08h Word Device interrupt routine offset.
// name 0Ah 8 Bytes Device name padded with spaces.
//
// Ref: https://www.infradeaorg/devload/DOSTables.html, Appendix 2
//
namespace DeviceDriverInfo {
constexpr uint8_t next_rpt_offset = 0x00; // DWORD, points to next driver in
// chain (x:FFFF means end)
constexpr uint8_t attributes_offset = 0x04; // WORD, device attributes offset
constexpr uint8_t strategy_offset = 0x06; // WORD, device strategy routine
// offset.
constexpr uint8_t interrupt_offset = 0x08; // WORD, device interrupt
// routine offset.
constexpr uint8_t name_offset = 0x0a; // 8 bytes, device name padded with
// spaces.
constexpr size_t name_length = 8;
} // namespace DeviceDriverInfo

// Indicates if the device at the given real pointer is the last in DOS's chain
bool DOS_IsLastDevice(const RealPt rp)
{
constexpr uint16_t last_offset_marker = 0xffff;
return RealOff(rp) == last_offset_marker;
}

// From the given real pointer, get the next device driver's real pointer
RealPt DOS_GetNextDevice(const RealPt rp)
{
return real_readd(RealSeg(rp),
RealOff(rp) + DeviceDriverInfo::next_rpt_offset);
}

// Get the tail real pointer from the DOS device driver linked list
RealPt DOS_GetLastDevice()
{
auto rp = dos_infoblock.GetDeviceChain();

while (!DOS_IsLastDevice(rp)) {
rp = DOS_GetNextDevice(rp);
}
return rp;
}

// Append the device at the given address to the end of the DOS device linked list
void DOS_AppendDevice(const uint16_t segment, const uint16_t offset)
{
const auto new_rp = RealMake(segment, offset);
const auto tail_rp = DOS_GetLastDevice();
real_writed(RealSeg(tail_rp), RealOff(tail_rp), new_rp);
}

bool DOS_DeviceHasAttributes(const RealPt rp, const uint16_t req_attributes)
{
const auto attributes = real_readw(
RealSeg(rp), RealOff(rp) + DeviceDriverInfo::attributes_offset);

return (attributes & req_attributes) == req_attributes;
}

uint16_t DOS_GetDeviceStrategy(const RealPt rp)
{
return real_readw(RealSeg(rp),
RealOff(rp) + DeviceDriverInfo::strategy_offset);
}

uint16_t DOS_GetDeviceInterrupt(const RealPt rp)
{
return real_readw(RealSeg(rp),
RealOff(rp) + DeviceDriverInfo::interrupt_offset);
}

bool DOS_DeviceHasName(const RealPt rp, const std::string_view req_name)
{
const auto segment = RealSeg(rp);
const auto offset = RealOff(rp) + DeviceDriverInfo::name_offset;

const auto search_len = std::min(req_name.length(),
DeviceDriverInfo::name_length);

for (uint8_t i = 0; i < search_len; ++i) {
// source and requested characters
const auto s = real_readb(segment, check_cast<uint16_t>(offset + i));
const auto r = req_name[i];
if (r == '\0') {
break;
}
if (r != static_cast<char>(s)) {
return false;
}
}
return true;
}

void DOS_SetupDevices() {
DOS_Device * newdev;
newdev=new device_CON();
Expand Down
10 changes: 1 addition & 9 deletions src/dos/dos_mscdex.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -351,15 +351,7 @@ int CMscdex::AddDrive(uint16_t _drive, char* physicalPath, uint8_t& subUnit)
devHeader.SetName ("MSCD001 ");

//Link it in the device chain
uint32_t start = dos_infoblock.GetDeviceChain();
uint16_t segm = (uint16_t)(start>>16);
uint16_t offm = (uint16_t)(start&0xFFFF);
while(start != 0xFFFFFFFF) {
segm = (uint16_t)(start>>16);
offm = (uint16_t)(start&0xFFFF);
start = real_readd(segm,offm);
}
real_writed(segm,offm,seg<<16);
DOS_AppendDevice(seg);

// Create Callback Strategy
uint16_t off = sizeof(DOS_DeviceHeader::sDeviceHeader);
Expand Down