diff options
Diffstat (limited to '')
21 files changed, 19558 insertions, 0 deletions
diff --git a/private/ntos/fw/duobase/mips/bldr.h b/private/ntos/fw/duobase/mips/bldr.h new file mode 100644 index 000000000..4af349b29 --- /dev/null +++ b/private/ntos/fw/duobase/mips/bldr.h @@ -0,0 +1,258 @@ +/*++ + +Copyright (c) 1991 Microsoft Corporation + +Module Name: + + bldr.h + +Abstract: + + This module is the header file for the NT boot loader. + +Author: + + David N. Cutler (davec) 10-May-1991 + +Revision History: + +--*/ + +#ifndef _BLDR_ +#define _BLDR_ + +#include "ntos.h" +#include "arccodes.h" +#include "fatboot.h" + + +// +// Define boot file id. +// + +#define BOOT_FILEID 2 // boot partition file id + +// +// Define image types. +// + +#define MIPS_IMAGE 0x162 + + +#define TARGET_IMAGE MIPS_IMAGE + +// +// Define size of sector. +// + +#define SECTOR_SIZE 512 // size of disk sector +#define SECTOR_SHIFT 9 // sector shift value + +// +// Define number of entries in file table. +// + +#define BL_FILE_TABLE_SIZE 8 + +// +// Define size of memory allocation table. +// + +#define BL_MEMORY_TABLE_SIZE 16 + +// +// Define buffer alignment macro. +// + +#define ALIGN_BUFFER(Buffer) (PVOID) \ + ((((ULONG)(Buffer) + KeGetDcacheFillSize() - 1)) & (~(KeGetDcacheFillSize() - 1))) + + +typedef +ARC_STATUS +(*PRENAME_ROUTINE)( + IN ULONG FileId, + IN PCHAR NewName + ); + +// +// Device entry table structure. +// + +typedef struct _BL_DEVICE_ENTRY_TABLE { + PARC_CLOSE_ROUTINE Close; + PARC_MOUNT_ROUTINE Mount; + PARC_OPEN_ROUTINE Open; + PARC_READ_ROUTINE Read; + PARC_READ_STATUS_ROUTINE GetReadStatus; + PARC_SEEK_ROUTINE Seek; + PARC_WRITE_ROUTINE Write; + PARC_GET_FILE_INFO_ROUTINE GetFileInformation; + PARC_SET_FILE_INFO_ROUTINE SetFileInformation; + PRENAME_ROUTINE Rename; + PARC_GET_DIRECTORY_ENTRY_ROUTINE GetDirectoryEntry; +} BL_DEVICE_ENTRY_TABLE, *PBL_DEVICE_ENTRY_TABLE; + +// +// Define partition context structure. +// + +typedef struct _PARTITION_CONTEXT { + LARGE_INTEGER PartitionLength; + ULONG StartingSector; + ULONG EndingSector; + UCHAR DiskId; + UCHAR DeviceUnit; + UCHAR TargetId; + UCHAR PathId; + ULONG SectorShift; + ULONG Size; + struct _DEVICE_OBJECT *PortDeviceObject; +} PARTITION_CONTEXT, *PPARTITION_CONTEXT; + +// +// Define serial port context structure +// +typedef struct _SERIAL_CONTEXT { + ULONG PortBase; + ULONG PortNumber; +} SERIAL_CONTEXT, *PSERIAL_CONTEXT; + +// +// Define drive context structure (for x86 BIOS) +// +typedef struct _DRIVE_CONTEXT { + ULONG Drive; + ULONG Cylinders; + ULONG Heads; + ULONG Sectors; +} DRIVE_CONTEXT, *PDRIVE_CONTEXT; + +// +// Define Floppy context structure +// +typedef struct _FLOPPY_CONTEXT { + ULONG DriveType; + ULONG SectorsPerTrack; + UCHAR DiskId; +} FLOPPY_CONTEXT, *PFLOPPY_CONTEXT; + +// +// Define keyboard context structure +// +typedef struct _KEYBOARD_CONTEXT { + BOOLEAN ScanCodes; +} KEYBOARD_CONTEXT, *PKEYBOARD_CONTEXT; + +// +// Define Console context +// +typedef struct _CONSOLE_CONTEXT + { + ULONG ConsoleNumber; + } CONSOLE_CONTEXT, *PCONSOLE_CONTEXT; + +// +// Define OMF header structure +// +typedef struct _OMF_HDR + { + UCHAR ID[4]; + USHORT FwSize; + UCHAR ReservedZ1[2]; + UCHAR ProductId[7]; + UCHAR ReservedZ2; + ULONG FolderCount; + UCHAR EisaVersion; + UCHAR EisaRevision; + UCHAR FwVersion; + UCHAR FwRevision; + UCHAR ChecksumByte; + UCHAR ReservedA[3]; + ULONG FolderDirectoryLink; + } OMF_HDR, *POMF_HDR; + +// +// Define OMF directory entry structure +// + +#define OMF_FILE_NAME_LEN 12 // 12 chars +typedef struct _OMF_DIR_ENT + { + UCHAR FolderName[ OMF_FILE_NAME_LEN ]; + UCHAR Reserved[2]; + UCHAR FolderType; + UCHAR FolderChecksumByte; + ULONG FolderSize; + ULONG FolderLink; + } OMF_DIR_ENT, *POMF_DIR_ENT; + +// +// Define OMF header file system context +// +typedef struct _OMF_HEADER_CONTEXT + { + ULONG FileId; + PULONG RomIndex; + PULONG RomRead; + OMF_HDR OmfHeader; + } OMF_HEADER_CONTEXT, *POMF_HEADER_CONTEXT; + +// +// Define "OMF file" file system context +// +typedef struct _OMF_FILE_CONTEXT + { + OMF_DIR_ENT OmfDirEnt; + } OMF_FILE_CONTEXT, *POMF_FILE_CONTEXT; + + +// +// Define file table structure. +// + +typedef struct _BL_FILE_FLAGS { + ULONG Open : 1; + ULONG Read : 1; + ULONG Write : 1; +} BL_FILE_FLAGS, *PBL_FILE_FLAGS; + +#define MAXIMUM_FILE_NAME_LENGTH 32 + +typedef struct _BL_FILE_TABLE { + BL_FILE_FLAGS Flags; + ULONG DeviceId; + LARGE_INTEGER Position; + PVOID StructureContext; + PBL_DEVICE_ENTRY_TABLE DeviceEntryTable; + UCHAR FileNameLength; + CHAR FileName[MAXIMUM_FILE_NAME_LENGTH]; + union { + FAT_FILE_CONTEXT FatFileContext; + PARTITION_CONTEXT PartitionContext; + SERIAL_CONTEXT SerialContext; + DRIVE_CONTEXT DriveContext; + FLOPPY_CONTEXT FloppyContext; + KEYBOARD_CONTEXT KeyboardContext; + CONSOLE_CONTEXT ConsoleContext; + OMF_FILE_CONTEXT OmfFileContext; + OMF_HEADER_CONTEXT OmfHeaderContext; + } u; +} BL_FILE_TABLE, *PBL_FILE_TABLE; + +// Define file structure recognition prototypes. +// + +PBL_DEVICE_ENTRY_TABLE +IsFatFileStructure ( + IN ULONG DeviceId, + IN PVOID StructureContext + ); + +// +// Define external references. +// + +extern BL_FILE_TABLE BlFileTable[BL_FILE_TABLE_SIZE]; + +#endif // _BLDR_ diff --git a/private/ntos/fw/duobase/mips/duobase.c b/private/ntos/fw/duobase/mips/duobase.c new file mode 100644 index 000000000..92e8c537e --- /dev/null +++ b/private/ntos/fw/duobase/mips/duobase.c @@ -0,0 +1,199 @@ +/*++ + +Copyright (c) 1991 Microsoft Corporation + +Module Name: + + duobase.c + +Abstract: + + This module is the entry point of the DUO Base Prom. + It initializes a subset of the firmware to be able to load + a file from the scsifloppy and execute it. + +Author: + + Lluis Abello (lluis) 5-Apr-1993 + +Revision History: + +--*/ + +#include "fwp.h" +#include "fat.h" +#include "string.h" +#ifdef DUO +#include "duoint.h" +#else +#include "jazzint.h" +#endif +#include "duobase.h" +extern PCHAR FwPoolBase; +extern PCHAR FwFreePool; + +typedef +VOID +(*PTRANSFER_ROUTINE) ( + ULONG Argc, + PCHAR Argv[] + ); + +typedef VOID (* LED_ROUTINE)(ULONG); + +#define PutLedDisplay ((LED_ROUTINE) PROM_ENTRY(14)) + + +ARC_STATUS +FwLoad ( + IN PCHAR ImagePath, + IN ULONG TopAddress, + OUT PULONG EntryAddress, + OUT PULONG LowAddress + ); + +VOID InitializeCacheVariables( + IN VOID + ); + +VOID +EEPromLoad( + ) +/*++ + +Routine Description: + + This routine scans the floppy scsi devices for a disk labeled + DUO_SETUP. + +Arguments: + + None. + +Return Value: + + It doesn't return. + +--*/ + +{ + + + ARC_STATUS Status; + ULONG FloppyDrive; + ULONG EntryPoint, BaseOfCode; + UCHAR PathName[128]; + PCHAR Argv[1]; + + // + // Initialize the cache variables used by the cache flushing routines. + // + InitializeCacheVariables(); + + // + // Initialize a subset of the firmware. This is mainly scsi driver and + // FAT file system. + // The screen and keyboard are not initialized. + // + + PutLedDisplay(0x11); + + FwInitialize(0); + + PutLedDisplay(0x33); + + for (FloppyDrive=0; FloppyDrive < SIZE_OF_LOOKUP_TABLE; FloppyDrive++) { + if (DeviceLookupTable[FloppyDrive].DevicePath != NULL) { + strcpy(PathName,DeviceLookupTable[FloppyDrive].DevicePath); + + PutLedDisplay(0x44); + + + strcat(PathName,"romsetup.exe"); + if (Status = FwLoad (PathName,0x400000,&EntryPoint,&BaseOfCode) == ESUCCESS) { + PutLedDisplay(0x55); + + // + // Call Loaded program and pass the DiskPathName as argument + // + strcpy(PathName,DeviceLookupTable[FloppyDrive].DevicePath); + Argv[0] = PathName; + ((PTRANSFER_ROUTINE)EntryPoint)(1,Argv); + PutLedDisplay(0x10066); + return; + } + } + } + PutLedDisplay(0x10077); +} + +VOID +FwInitialize ( + IN ULONG MemSize + ) + +/*++ + +Routine Description: + + This routine initializes the system parameter block which is located + in low memory. This structure contains the firmware entry vector and + the restart parameter block. This routine also initializes the io devices, + the configuration, and opens standard in/out. + + Note: the system parameter block is initialized early in selftest.c so that + the video prom can update any required vendor entries. + +Arguments: + + MemSize - Not Used. For compatibility with definitions in bldr\firmware.h + +Return Value: + + None. + +--*/ + +{ + ULONG TTBase; + + PutLedDisplay(0); + // + // Initialize pointers and zero memory for the allocate pool routine. + // + + FwPoolBase = (PCHAR)FW_POOL_BASE; + FwFreePool = (PCHAR)FW_POOL_BASE; + RtlZeroMemory(FwPoolBase, FW_POOL_SIZE); + + + // + // Initialize the DMA translation table base address and limit. + // + + TTBase = (ULONG) FwAllocatePool(PAGE_SIZE); + WRITE_REGISTER_ULONG(&DMA_CONTROL->TranslationBase.Long,TTBase); + WRITE_REGISTER_ULONG(&DMA_CONTROL->TranslationLimit.Long, PAGE_SIZE); + + // + // Disable the I/O device interrupts. + // + + WRITE_REGISTER_USHORT(&((PINTERRUPT_REGISTERS)INTERRUPT_VIRTUAL_BASE)->Enable,0); + + // + // Initialize IO structures + // + + FwIoInitialize1(); + PutLedDisplay(0x99); + + + // + // Initialize the I/O services and environment. + // + + FwIoInitialize2(); + PutLedDisplay(0x88); + return; +} diff --git a/private/ntos/fw/duobase/mips/duobase.h b/private/ntos/fw/duobase/mips/duobase.h new file mode 100644 index 000000000..b9e828b54 --- /dev/null +++ b/private/ntos/fw/duobase/mips/duobase.h @@ -0,0 +1,66 @@ +/*++ + +Copyright (c) 1990 Microsoft Corporation + +Module Name: + + duobase.h + +Abstract: + + This file contains the definitions to read and write IO registers. + +Author: + + Lluis Abello (lluis) 1-May-91 + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#ifndef _DUOBASE +#define _DUOBASE + +// +// Remove scsi debug print to avoid useless messages in the prom +// +//#undef ScsiDebugPrint +#define ScsiDebugPrint(a,b,c,d,e,f,g) + + + +#undef READ_REGISTER_UCHAR +#undef READ_REGISTER_USHORT +#undef READ_REGISTER_ULONG +#undef WRITE_REGISTER_UCHAR +#undef WRITE_REGISTER_USHORT +#undef WRITE_REGISTER_ULONG + + +// +// define ScsiPort Write/Read macros +// +#define ScsiPortReadPortUchar(x) READ_REGISTER_UCHAR(x) +#define ScsiPortReadPortUshort(x) READ_REGISTER_USHORT(x) +#define ScsiPortReadPortUlong(x) READ_REGISTER_ULONG(x) + +#define ScsiPortWritePortUchar(x,y) WRITE_REGISTER_UCHAR(x, y) +#define ScsiPortWritePortUshort(x,y) WRITE_REGISTER_USHORT(x, y) +#define ScsiPortWritePortUlong(x,y) WRITE_REGISTER_ULONG(x, y) + + +#define READ_REGISTER_UCHAR(x) *(volatile UCHAR * const)(x) +#define READ_REGISTER_USHORT(x) *(volatile USHORT * const)(x) +#define READ_REGISTER_ULONG(x) *(volatile ULONG * const)(x) + +#define WRITE_REGISTER_UCHAR(x, y) *(volatile UCHAR * const)(x) = y +#define WRITE_REGISTER_USHORT(x, y) *(volatile USHORT * const)(x) = y +#define WRITE_REGISTER_ULONG(x, y) *(volatile ULONG * const)(x) = y + + + +#endif diff --git a/private/ntos/fw/duobase/mips/duoreset.h b/private/ntos/fw/duobase/mips/duoreset.h new file mode 100644 index 000000000..064a616b5 --- /dev/null +++ b/private/ntos/fw/duobase/mips/duoreset.h @@ -0,0 +1,121 @@ +/*++ + +Copyright (c) 1990 Microsoft Corporation + +Module Name: + + duoreset.h + +Abstract: + + This file defines various constants for the Base prom reset module. + +Author: + + Lluis Abello (lluis) 28-Apr-1993 + +Revision History: + +--*/ + +#ifndef _DUORESET_ +#define _DUORESET_ + +//TEMPTEMP + +#define SECONDARY_CACHE_SIZE (1 << 20) +#define SECONDARY_CACHE_INVALID 0x0 +#define TAGLO_SSTATE 0xA +#define INDEX_FILL_I 0x14 // ****** temp ****** this must be moved to kxmips.h +#define HIT_WRITEBACK_I 0x18 // ****** temp ****** this must be moved to kxmips.h + +// +// redefine bal to be a relative branch and link instead of jal as it's +// defined in kxmips.h. This allows calling routines when running in either +// ROM_VIRT Addresses or ResetVector Addresses. +// The cpp will issue a redefinition warning message. +// + +#define bal bgezal zero, + +// +// #define MCTADR register values. +// + +// +// Define remspeed registers. +// +#ifndef DUO +#define REMSPEED0 7 // reserved +#define REMSPEED1 0 // Ethernet +#define REMSPEED2 1 // scsi +#define REMSPEED3 2 // floppy +#define REMSPEED4 7 // rtc +#define REMSPEED5 3 // kbd/mouse +#define REMSPEED6 2 // serial 1 +#define REMSPEED7 2 // serial 2 +#define REMSPEED8 2 // parallel +#define REMSPEED9 4 // nvram +#define REMSPEED10 1 // interrupt src +#define REMSPEED11 2 // PROM (should be 4) +#define REMSPEED12 1 // sound +#define REMSPEED13 7 // new device +#define REMSPEED14 1 // EISA latch +#define REMSPEED15 1 // led +#else +#define REMSPEED0 7 // reserved +#define REMSPEED1 0 // Ethernet +#define REMSPEED2 0 // scsi +#define REMSPEED3 0 // scsi +#define REMSPEED4 7 // rtc +#define REMSPEED5 3 // kbd/mouse +#define REMSPEED6 2 // serial 1 +#define REMSPEED7 2 // serial 2 +#define REMSPEED8 2 // parallel +#define REMSPEED9 4 // nvram +#define REMSPEED10 3 // interrupt src +#define REMSPEED11 3 // PROM (should be 4) +#define REMSPEED12 7 // new device +#define REMSPEED13 7 // new device +#define REMSPEED14 1 // LED +#endif + + +#define PROM_BASE (KSEG1_BASE | 0x1fc00000) +#define PROM_ENTRY(x) (PROM_BASE + ((x) * 8)) + + +// +// Define addresses +// + +#define LINK_ADDRESS 0xE1000000 +#define RESET_VECTOR 0xBFC00000 + +#define STACK_SIZE 0xA000 // 40Kb of stack +#define RAM_TEST_STACK_ADDRESS 0xA000BFF0 // Stack for code copied to memory + +// +// Address definitions for the Basic firmware written in C. +// The code is copied to RAM_TEST_LINK_ADDRESS so that it runs at the address +// it was linked. +// + +#define RAM_TEST_DESTINATION_ADDRESS 0xA000C000 // uncached link address +#define RAM_TEST_LINK_ADDRESS 0xA000C000 // Link Address of code + +#define EISA_MEMORY_PHYSICAL_BASE_PAGE 0x100000 + + +// +// define Video ROM addresses. +// +#define VIDEO_PROM_CODE_VIRTUAL_BASE 0x10000000 // Link address of video prom code +#define VIDEO_PROM_CODE_PHYSICAL_BASE 0x50000 // phys address of video prom code +#define VIDEO_PROM_CODE_UNCACHED_BASE (KSEG1_BASE + VIDEO_PROM_CODE_PHYSICAL_BASE) // uncached address where video prom code is copied + +#define VIDEO_PROM_SIZE 0x10000 + +#define FW_FONT_ADDRESS (KSEG0_BASE + VIDEO_PROM_CODE_PHYSICAL_BASE + VIDEO_PROM_SIZE) + +#endif // _DUORESET_ diff --git a/private/ntos/fw/duobase/mips/duoreset.s b/private/ntos/fw/duobase/mips/duoreset.s new file mode 100644 index 000000000..dc7c48b28 --- /dev/null +++ b/private/ntos/fw/duobase/mips/duoreset.s @@ -0,0 +1,1942 @@ +#if defined(R4000) // && defined(DUO) +/*++ + +Copyright (c) 1993 Microsoft Corporation + +Module Name: + + duoreset.s + +Abstract: + + This module is the start-up code for the Base prom code. + This code will be the first run upon reset. + + This module contains the BEV exception vectors. + + On a hard reset the minimal initialization to be able to + read the keyboard is done. If a key is being pressed, the + scsi floppy drive is looked up and the setup program loaded. + + If it's a soft reset or NMI Map The FLASH_PROM in the TLB and jump to + the FLASH PROM Reset vector. + +Author: + + Lluis Abello (lluis) 8-Apr-93 + +Environment: + + Executes in kernal mode. + +Notes: + + ***** IMPORTANT ***** + + This module must be linked such that it resides in the + first page of the rom. + +Revision History: + + +--*/ +// +// include header file +// +#include <ksmips.h> +#ifdef DUO +#include <duoprom.h> +#else +#include <jazzprom.h> +#define EEPROM_VIRTUAL_BASE PROM_VIRTUAL_BASE +#define FLASH_PROM_TLB_INDEX 0 +#endif + +#include "duoreset.h" +#include "dmaregs.h" +#include "led.h" +#include "kbdmouse.h" + +#define TLB_HI 0 +#define TLB_LO0 4 +#define TLB_LO1 8 +#define TLB_MASK 12 + + + +//TEMPTEMP + +#define COPY_ENTRY 6 + +.text +.set noreorder +.set noat + + + ALTERNATE_ENTRY(ResetVector) + ori zero,zero,0xffff // this is a dummy instruction to + // fix a bug where the first byte + // fetched from the PROM is wrong + b ResetException + nop + +// +// This is the jump table for rom routines that other +// programs can call. They are placed here so that they +// will be unlikely to move. +// +// +// This becomes PROM_ENTRY(2) as defined in ntmips.h +// + .align 4 + nop +// +// Entries 4 to 7 are used for the ROM Version and they +// must be zero in this file. +// + +// +// This becomes PROM_ENTRYS(8,9...) +// + .align 6 + nop // entry 8 + nop + nop // entry 9 + nop + b TlbInit // entry 10 + nop + nop // entry 11 + nop + nop // entry 12 + nop + nop // entry 13 + nop + b PutLedDisplay // entry 14 + nop + nop // entry 15 + nop + nop // entry 16 + nop + + +// +// This table contains the default values for the remote speed regs. +// +RomRemoteSpeedValues: + .byte REMSPEED1 // ethernet + .byte REMSPEED2 // SCSI + .byte REMSPEED3 // Floppy / SCSI (DUO) + .byte REMSPEED4 // RTC + .byte REMSPEED5 // Kbd/Mouse + .byte REMSPEED6 // Serial port 1 + .byte REMSPEED7 // Serial port 2 + .byte REMSPEED8 // Parallel + .byte REMSPEED9 // NVRAM + .byte REMSPEED10 // Int src reg + .byte REMSPEED11 // PROM + .byte REMSPEED12 // Sound / New Dev (DUO) + .byte REMSPEED13 // New dev + .byte REMSPEED14 // External Eisa latch / LED (DUO) + + +// +// New TLB Entries can be added to the following table +// The format of the table is: +// entryhi; entrylo0; entrylo1; pagemask +// + .align 4 +TlbEntryTable: + +// +// 256KB Base PROM +// 256KB Flash PROM +// + + .word ((PROM_VIRTUAL_BASE >> 13) << ENTRYHI_VPN2) + .word ((PROM_PHYSICAL_BASE >> 12) << ENTRYLO_PFN) + (1 << ENTRYLO_G) + \ + (1 << ENTRYLO_V) + (2 << ENTRYLO_C) + (1 << ENTRYLO_D) + + .word (((PROM_PHYSICAL_BASE+0x40000) >> 12) << ENTRYLO_PFN) + (1 << ENTRYLO_G) + \ + (1 << ENTRYLO_V) + (2 << ENTRYLO_C) + (1 << ENTRYLO_D) + + .word (PAGEMASK_256KB << PAGEMASK_PAGEMASK) +// +// I/O Device space non-cached, valid, dirty +// + .word ((DEVICE_VIRTUAL_BASE >> 13) << ENTRYHI_VPN2) + .word ((DEVICE_PHYSICAL_BASE >> 12) << ENTRYLO_PFN) + (1 << ENTRYLO_G) + \ + (1 << ENTRYLO_V) + (1 << ENTRYLO_D) + (2 << ENTRYLO_C) + .word (1 << ENTRYLO_G) // set global bit even if page not used + .word (PAGEMASK_64KB << PAGEMASK_PAGEMASK) + +#ifndef DUO +// +// Interrupt source register space +// non-cached - read/write +// + .word ((INTERRUPT_VIRTUAL_BASE >> 13) << ENTRYHI_VPN2) + .word ((INTERRUPT_PHYSICAL_BASE >> 12) << ENTRYLO_PFN) + (1 << ENTRYLO_G) + \ + (1 << ENTRYLO_D) + (1 << ENTRYLO_V) + (2 << ENTRYLO_C) + .word (1 << ENTRYLO_G) // set global bit even if page not used + .word (PAGEMASK_4KB << PAGEMASK_PAGEMASK) +#endif + + +// +// video control 2MB non-cached read/write. +// + .word ((VIDEO_CONTROL_VIRTUAL_BASE >> 13) << ENTRYHI_VPN2) + .word ((VIDEO_CONTROL_PHYSICAL_BASE >> 12) << ENTRYLO_PFN) + (1 << ENTRYLO_G) + \ + (1 << ENTRYLO_V) + (1 << ENTRYLO_D) + (2 << ENTRYLO_C) + .word (((VIDEO_CONTROL_PHYSICAL_BASE+0x100000) >> 12) << ENTRYLO_PFN) + (1 << ENTRYLO_G) + \ + (1 << ENTRYLO_V) + (1 << ENTRYLO_D) + (2 << ENTRYLO_C) + .word (PAGEMASK_1MB << PAGEMASK_PAGEMASK) +// +// extended video control 2MB non-cached read/write. +// + .word ((EXTENDED_VIDEO_CONTROL_VIRTUAL_BASE >> 13) << ENTRYHI_VPN2) + .word ((EXTENDED_VIDEO_CONTROL_PHYSICAL_BASE >> 12) << ENTRYLO_PFN) + (1 << ENTRYLO_G) + \ + (1 << ENTRYLO_V) + (1 << ENTRYLO_D) + (2 << ENTRYLO_C) + .word (((EXTENDED_VIDEO_CONTROL_PHYSICAL_BASE+0x100000) >> 12) << ENTRYLO_PFN) + (1 << ENTRYLO_G) + \ + (1 << ENTRYLO_V) + (1 << ENTRYLO_D) + (2 << ENTRYLO_C) + .word (PAGEMASK_1MB << PAGEMASK_PAGEMASK) +// +// video memory space 8Mb non-cached read/write +// + .word ((VIDEO_MEMORY_VIRTUAL_BASE >> 13) << ENTRYHI_VPN2) + .word ((VIDEO_MEMORY_PHYSICAL_BASE >> 12) << ENTRYLO_PFN) + (1 << ENTRYLO_G) + \ + (1 << ENTRYLO_V) + (1 << ENTRYLO_D) + (2 << ENTRYLO_C) + .word (((VIDEO_MEMORY_PHYSICAL_BASE+0x400000) >> 12) << ENTRYLO_PFN) + (1 << ENTRYLO_G) + \ + (1 << ENTRYLO_V) + (1 << ENTRYLO_D) + (2 << ENTRYLO_C) + .word (PAGEMASK_4MB << PAGEMASK_PAGEMASK) +// +// EISA I/O 16Mb non-cached read/write +// EISA MEM 16Mb non-cached read/write +// + .word ((EISA_IO_VIRTUAL_BASE >> 13) << ENTRYHI_VPN2) + .word ((EISA_IO_PHYSICAL_BASE >> 12) << ENTRYLO_PFN) + (1 << ENTRYLO_G) + \ + (1 << ENTRYLO_V) + (1 << ENTRYLO_D) + (2 << ENTRYLO_C) + .word ((EISA_MEMORY_PHYSICAL_BASE_PAGE) << ENTRYLO_PFN) + (1 << ENTRYLO_G) + \ + (1 << ENTRYLO_V) + (1 << ENTRYLO_D) + (2 << ENTRYLO_C) + .word (PAGEMASK_16MB << PAGEMASK_PAGEMASK) +// +// EISA I/O page 0 non-cached read/write +// EISA I/O page 1 non-cached read/write +// + .word ((EISA_EXTERNAL_IO_VIRTUAL_BASE >> 13) << ENTRYHI_VPN2) + .word ((0 >> 12) << ENTRYLO_PFN) + (1 << ENTRYLO_G) + \ + (1 << ENTRYLO_V) + (1 << ENTRYLO_D) + (2 << ENTRYLO_C) + .word (((EISA_IO_PHYSICAL_BASE + 1 * PAGE_SIZE) >> 12) << ENTRYLO_PFN) + (1 << ENTRYLO_G) + \ + (1 << ENTRYLO_V) + (1 << ENTRYLO_D) + (2 << ENTRYLO_C) + .word (PAGEMASK_4KB << PAGEMASK_PAGEMASK) +// +// EISA I/O page 2 non-cached read/write +// EISA I/O page 3 non-cached read/write +// + .word (((EISA_EXTERNAL_IO_VIRTUAL_BASE + 2 * PAGE_SIZE) >> 13) << ENTRYHI_VPN2) + .word (((EISA_IO_PHYSICAL_BASE + 2 * PAGE_SIZE) >> 12) << ENTRYLO_PFN) + (1 << ENTRYLO_G) + \ + (1 << ENTRYLO_V) + (1 << ENTRYLO_D) + (2 << ENTRYLO_C) + .word (((EISA_IO_PHYSICAL_BASE + 3 * PAGE_SIZE) >> 12) << ENTRYLO_PFN) + (1 << ENTRYLO_G) + \ + (1 << ENTRYLO_V) + (1 << ENTRYLO_D) + (2 << ENTRYLO_C) + .word (PAGEMASK_4KB << PAGEMASK_PAGEMASK) +// +// EISA I/O page 4 non-cached read/write +// EISA I/O page 5 non-cached read/write +// + .word (((EISA_EXTERNAL_IO_VIRTUAL_BASE + 4 * PAGE_SIZE) >> 13) << ENTRYHI_VPN2) + .word (((EISA_IO_PHYSICAL_BASE + 4 * PAGE_SIZE) >> 12) << ENTRYLO_PFN) + (1 << ENTRYLO_G) + \ + (1 << ENTRYLO_V) + (1 << ENTRYLO_D) + (2 << ENTRYLO_C) + .word (((EISA_IO_PHYSICAL_BASE + 5 * PAGE_SIZE) >> 12) << ENTRYLO_PFN) + (1 << ENTRYLO_G) + \ + (1 << ENTRYLO_V) + (1 << ENTRYLO_D) + (2 << ENTRYLO_C) + .word (PAGEMASK_4KB << PAGEMASK_PAGEMASK) +// +// EISA I/O page 6 non-cached read/write +// EISA I/O page 7 non-cached read/write +// + .word (((EISA_EXTERNAL_IO_VIRTUAL_BASE + 6 * PAGE_SIZE) >> 13) << ENTRYHI_VPN2) + .word (((EISA_IO_PHYSICAL_BASE + 6 * PAGE_SIZE) >> 12) << ENTRYLO_PFN) + (1 << ENTRYLO_G) + \ + (1 << ENTRYLO_V) + (1 << ENTRYLO_D) + (2 << ENTRYLO_C) + .word (((EISA_IO_PHYSICAL_BASE + 7 * PAGE_SIZE) >> 12) << ENTRYLO_PFN) + (1 << ENTRYLO_G) + \ + (1 << ENTRYLO_V) + (1 << ENTRYLO_D) + (2 << ENTRYLO_C) + .word (PAGEMASK_4KB << PAGEMASK_PAGEMASK) +// +// EISA I/O pages 8,9,a,b non-cached read/write +// EISA I/O pages c,d,e,f non-cached read/write +// + .word (((EISA_EXTERNAL_IO_VIRTUAL_BASE + 8 * PAGE_SIZE) >> 13) << ENTRYHI_VPN2) + .word (((EISA_IO_PHYSICAL_BASE + 8 * PAGE_SIZE) >> 12) << ENTRYLO_PFN) + (1 << ENTRYLO_G) + \ + (1 << ENTRYLO_V) + (1 << ENTRYLO_D) + (2 << ENTRYLO_C) + .word (((EISA_IO_PHYSICAL_BASE + 12 * PAGE_SIZE) >> 12) << ENTRYLO_PFN) + (1 << ENTRYLO_G) + \ + (1 << ENTRYLO_V) + (1 << ENTRYLO_D) + (2 << ENTRYLO_C) + .word (PAGEMASK_16KB << PAGEMASK_PAGEMASK) + +// +// Map 64KB of memory for the video prom code&data cached. +// + .word ((VIDEO_PROM_CODE_VIRTUAL_BASE >> 13) << ENTRYHI_VPN2) + .word ((VIDEO_PROM_CODE_PHYSICAL_BASE >> 12) << ENTRYLO_PFN) + (1 << ENTRYLO_G) + \ + (1 << ENTRYLO_V) + (1 << ENTRYLO_D) + (3 << ENTRYLO_C) + .word (1 << ENTRYLO_G) // set global bit even if page not used + .word (PAGEMASK_64KB << PAGEMASK_PAGEMASK) +// +// Map 4kb of exclusive memory and 4Kb of shared +// + .word ((EXCLUSIVE_PAGE_VIRTUAL_BASE >> 13) << ENTRYHI_VPN2) + .word ((EXCLUSIVE_PAGE_PHYSICAL_BASE >> 12) << ENTRYLO_PFN) + (1 << ENTRYLO_G) + \ + (1 << ENTRYLO_V) + (1 << ENTRYLO_D) + (4 << ENTRYLO_C) + .word ((SHARED_PAGE_PHYSICAL_BASE >> 12) << ENTRYLO_PFN) + (1 << ENTRYLO_G) + \ + (1 << ENTRYLO_V) + (1 << ENTRYLO_D) + (5 << ENTRYLO_C) + .word (PAGEMASK_4KB << PAGEMASK_PAGEMASK) + +// +// Map PCR for kernel debugger. +// + .word ((PCR_VIRTUAL_BASE >> 13) << ENTRYHI_VPN2) + .word (1 << ENTRYLO_G) // set global bit even if page not used + .word ((PCR_PHYSICAL_BASE >> 12) << ENTRYLO_PFN) + (1 << ENTRYLO_G) + \ + (1 << ENTRYLO_V) + (1 << ENTRYLO_D) + (2 << ENTRYLO_C) + .word (PAGEMASK_4KB << PAGEMASK_PAGEMASK) + + +TlbEntryEnd: + .byte 0 + + + +/*++ +UserTlbMissHandler(); +Routine Description: + + + This becomes the entry point of a User TLB Miss Exception. + It should be located at address BFC00200 + + Jump to the handler in the Flash PROM + +Arguments: + + None. + +Return Value: + + None. + +--*/ + .align 9 + LEAF_ENTRY(UserTlbMiss200) + li k0,EEPROM_VIRTUAL_BASE+0x200// Load address of UTBMiss handler in flash prom + j k0 // Jump to Flash PROM Handler. + nop + .end UserTlbMiss200 + + +/*++ + +Routine Description: + + This routine will initialize the TLB for virtual addressing. + It sets the TLB according to a table of TLB entries. + + All other unused TLB entries will be zeroed and therefore invalidated. + + N.B. This routine must be loaded in the first page of the rom and must + be called using BFC00XXXX addresses. + +Arguments: + + a0 - supplies the base address of the table. + a1 - supplies the end address of the table + a2 - supplies the index of the first tlb entry to set the table into. + + +Return Value: + + None. + +Revision History: + +--*/ + LEAF_ENTRY(TlbInit) +// +// zero the whole TLB +// + mtc0 zero,entrylo0 // tag data to store + mtc0 zero,entrylo1 + li t0,KSEG0_BASE // set entry hi + mtc0 t0,entryhi + mtc0 zero,pagemask + move v0,zero // tlb entry index + li t0,48 << INDEX_INDEX // get last index + + mtc0 v0,index // entry pointer +tlbzeroloop: + addiu v0,v0,1<<INDEX_INDEX // increment counter + tlbwi // store it + bne v0,t0,tlbzeroloop // loop if less than max entries + mtc0 v0,index // entry pointer +10: + lw t3, TLB_HI(a0) // get entryhi + lw t4, TLB_LO0(a0) // get entrylo0 + lw t5, TLB_LO1(a0) // get entrylo1 + lw t6, TLB_MASK(a0) // get pagemask + mtc0 t3,entryhi // write entryhi + mtc0 t4,entrylo0 // write entrylo0 + mtc0 t5,entrylo1 // write entrylo1 + mtc0 t6,pagemask // write pagemask + mtc0 a2,index // write index + addiu a2,a2,1 << INDEX_INDEX // compute index for next tlb entry + tlbwi // write tlb entry + addiu a0,a0,16 // set pointer to next entry + bne a0,a1,10b // if not last go for next + nop + j ra + move v0,a2 // return index of next free tb entry + .end TlbInit + +/*++ +ParityHandler(); +Routine Description: + + + This becomes the entry point of a Cache Error Exception. + It should be located at address BFC00300 + + The system can call this routine when a cache error exception + is taken. At this point the state of the TLB is unknown. + Map the FlashProm in the TLB. + Jump to the handler in the Flash PROM + +Arguments: + + None. + +Return Value: + + None. + +--*/ + .align 8 + LEAF_ENTRY(ParityHandler300) +// +// Probe tlb for FLASH PROM address +// + la k0,TlbEntryTable - LINK_ADDRESS + RESET_VECTOR + lw k1,TLB_HI(k0) // get entryhi + mtc0 k1,entryhi // write entryhi + nop // 3 cycle hazard + nop // + nop // + tlbp // probe tlb. + nop // 2 cycle hazard + nop // + mfc0 k1,index // get result of probe + nop // 1 cycle hazard + bgez k1,10f // branch if entry in tlb + nop + li k1,FLASH_PROM_TLB_INDEX // get tlb index to map Flash prom + mtc0 k1,index // write index register +10: + lw k1,TLB_LO0(k0) // get entrylo0 + mtc0 k1,entrylo0 // write entrylo0 + lw k1,TLB_LO1(k0) // get entrylo1 + mtc0 k1,entrylo1 // write entrylo1 + lw k1,TLB_MASK(k0) // get pagemask + mtc0 k1,pagemask // write pagemask + nop // 1 cycle hazard + tlbwi // write tlb entry + nop + li k0,EEPROM_VIRTUAL_BASE+0x300// Load address of Parity Handler in flash prom + j k0 // Jump to Flash PROM Handler. + nop + .end + +/*++ +GeneralExceptionHandler(); +Routine Description: + + This becomes the entry point of a General Exception. + It should be located at address BFC00380 + Jump to the Handler in the FlashProm + +Arguments: + + None + +Return Value: + + None + +--*/ + .align 7 + LEAF_ENTRY(GeneralException380) + li k0,EEPROM_VIRTUAL_BASE+0x380// Load address of Parity Handler in flash prom + j k0 // Jump to Flash PROM Handler. + nop + .end GeneralException380 + + + +/*++ +ResetException + +Routine Description: + + + This becomes the entry point for a Reset or NMI Exception. + It should be located at address BFC00000 + + if SR bit in psr (Soft reset or NMI) + Map THE FLASH PROM + Jump to FLASH PROM Reset Vector (Base of Flash Prom) + else (hard reset) + Map Devices in tlb. + if the space bar key is being pressed + if the floppy contains a valid setup program. + Reinitialize The flash PROM from the floppy image. + Reboot Soft Reset. + end if + end if + end if; + +Arguments: + + None. + +Return Value: + + None. + +--*/ + ALTERNATE_ENTRY(ResetException) + +// +// Check cause of exception, if SR bit in PSR is set treat it as a soft reset +// or an NMI, otherwise it's a cold reset. +// + mfc0 k0,psr // get cause register + li k1,(1<<PSR_SR) // bit indicates soft reset. + and k1,k1,k0 // mask PSR with SR bit + mtc0 zero,watchlo // initialize the watch + mtc0 zero,watchhi // address registers + beq k1,zero,ColdReset // go if cold reset + + +// +// Probe tlb for FLASH PROM address +// + la k0,TlbEntryTable - LINK_ADDRESS + RESET_VECTOR + lw k1,TLB_HI(k0) // get entryhi + mtc0 k1,entryhi // write entryhi + nop // 3 cycle hazard + nop // + nop // + tlbp // probe tlb. + nop // 2 cycle hazard + nop // + mfc0 k1,index // get result of probe + nop // 1 cycle hazard + bgez k1,10f // branch if entry in tlb + nop + li k1,FLASH_PROM_TLB_INDEX // get tlb index to map Flash prom + mtc0 k1,index // write index register +10: + lw k1,TLB_LO0(k0) // get entrylo0 + mtc0 k1,entrylo0 // write entrylo0 + lw k1,TLB_LO1(k0) // get entrylo1 + mtc0 k1,entrylo1 // write entrylo1 + lw k1,TLB_MASK(k0) // get pagemask + mtc0 k1,pagemask // write pagemask + nop // 1 cycle hazard + tlbwi // write tlb entry + nop + li k0,EEPROM_VIRTUAL_BASE+0x000// Load address of Reset Vector in flash prom + j k0 // Jump to Flash PROM Handler. + nop + +ColdReset: + // + // Initialize TLB. + // + la a0, TlbEntryTable - LINK_ADDRESS + RESET_VECTOR + la a1, TlbEntryEnd - LINK_ADDRESS + RESET_VECTOR + bal TlbInit + li a2, COPY_ENTRY+1 // tlb entry index + + la k0,HardResetException // Branch to PROM Virtual Address + j k0 + nop + + + +/*++ + +HardResetException: + +Routine Description: + + The IO Devices are already mapped. + Initialize the keyboard, if no key is being pressed, jump to the base + Reset vector (Base) in the FLASH PROM. + If a key is being pressed. + Initialize 64KB of Memory. + Read SCSI Floppy. + Load Setup Program. + +Arguments: + + None. + +Return Value: + + None. + +--*/ + + ALTERNATE_ENTRY(HardResetException) + + bal PutLedDisplay // Blank the LED + ori a0,zero,LED_BLANK<<TEST_SHIFT + + bal InitKeyboardController + nop + + // + // If return value = TRUE, the Keyboard was NOT initialized. + // Branch to the FLASH prom in hope that a message can be printed out. + // + bne v0,zero,NoKey + + // + // Read a key from the keyboard. + // To check if space bar is pressed. + // + li a0,600 // timeout value + bal GetKbdData // Read kbd data + nop // + bne v0,zero,NoKey // if timeout No Keyis being pressed. + li t0,0xE0 // Load Delete key scan code. + beq v1,t0,FlashProm // if pressed go to FlashProm +NoKey: + + li k0,EEPROM_VIRTUAL_BASE+0x000// Load address of Reset Vector in flash prom + j k0 // Jump to Flash PROM Handler. + nop + + + +// +// +// If control reaches here. The space bar key was pressed to indicate +// that the Flash Prom needs to be updated. +// Initialize RemoteSpeed registers +// Initialize the processor +// Initialize the first 4 MB of Memory +// Copy the Scsi code to memory and jump to it. +// +// +// + +FlashProm: + + bal PutLedDisplay // Show a 1 in the LED + ori a0,zero,0x11 // + + + // + // Initialize psr + // + li k0,(1<<PSR_BEV) | (1 << PSR_CU1) | (1<<PSR_ERL) + mtc0 k0,psr // Clear interrupt bit while ERL still set + nop + li k0,(1<<PSR_BEV) | (1 << PSR_CU1) + nop + mtc0 k0,psr // Clear ERL bit + + + // + // Initialize config register + // + + mfc0 t0,config +#ifndef DUO + li t1, (1 << CONFIG_IB) + (0 << CONFIG_DB) + (0 << CONFIG_CU) + \ + (3 << CONFIG_K0) + + srl t2,t0,CONFIG_SC // check for secondary cache + and t2,t2,1 // isolate the bit + bne t2,zero,10f // if none, block size stays 32 bytes + srl t2,t0,CONFIG_SB // check secondary cache block size + and t2,t2,3 // isolate the bit + bne t2,zero,10f // if not 16 bytes, size stays 32 bytes + nop + + li t1, (0 << CONFIG_IB) + (0 << CONFIG_DB) + (0 << CONFIG_CU) + \ + (3 << CONFIG_K0) +#else + li t1, (1 << CONFIG_IB) + (1 << CONFIG_DB) + (0 << CONFIG_CU) + \ + (3 << CONFIG_K0) +#endif +10: + li t2, 0xFFFFFFC0 + and t0,t0,t2 // clear soft bits in config + or t0,t0,t1 // set soft bits in config + mtc0 t0,config + nop + nop + + bal PutLedDisplay // BLANK the LED + ori a0,zero,LED_BLANK<<TEST_SHIFT + + li s0,DMA_VIRTUAL_BASE + + ctc1 zero,fsr // clear floating status + +// +// Initialize remote speed registers. +// + addiu t1,s0,DmaRemoteSpeed1 // address of REM_SPEED 1 + la a1,RomRemoteSpeedValues // + addiu t2,a1,14 // addres of last value +WriteNextRemSpeed: + lbu v0,0(a1) // load init value for rem speed + addiu a1,a1,1 // compute next address + sw v0,0(t1) // write to rem speed reg + bne a1,t2,WriteNextRemSpeed // check for end condition + addiu t1,t1,8 // next register address + +// +// Initialize global config +// + li t1,0x5 // 2Mb Video Map Prom + sw t1,DmaConfiguration(s0) // Init Global Config + +// +// Test the first 8kb of memory. +// +TestMemory: + bal PutLedDisplay // call PutLedDisplay to show that + ori a0,zero,LED_MEMORY_TEST_1 // Mem test is starting + +// +// Disable Parity exceptions for the first memory test. Otherwise +// if something is wrong with the memory we jump to the moon. +// +// DUO ECC diag register resets to zero which forces good ecc to be +// written during read modify write cycles. +// + li t0, (1<<PSR_DE) | (1 << PSR_CU1) | (1 << PSR_BEV) + mtc0 t0,psr + nop + li a0,KSEG1_BASE // base of memory to test non cached. + li a1,0x2000 // length to test in bytes + bal MemoryTest // Branch to routine + ori a2,zero,LED_MEMORY_TEST_1 // set Test/Subtest ID + +// +// The next step is to copy a number of routines to memory so they can +// be executed more quickly. Calculate the arguments for DataCopy call: +// a0 is source of data, a1 is dest, a2 is length in bytes +// + la a0,MemoryRoutines // source + la a1,MemoryRoutines-LINK_ADDRESS+KSEG1_BASE // destination location + la t2,EndMemoryRoutines // end + bal DataCopy // copy code to memory + sub a2,t2,a0 // length of code + +// +// Call cache initialization routine in non-cached memory +// + bal PutLedDisplay // display that cache init + ori a0,zero,LED_CACHE_INIT // is starting + la s1,R4000CacheInit-LINK_ADDRESS+KSEG1_BASE // non-cached address + jal s1 // initialize caches + nop + +// +// Test and initialize the first 4 megs of memory +// Running cached. +// + bal PutLedDisplay // call PutLedDisplay to show that + ori a0,zero,LED_MEMORY_TEST_1 // Mem test is starting + li a0,KSEG1_BASE+0x2000 // base of memory to test non cached. + li a1,0x400000-0x2000 // length to test in bytes + la s1,MemoryTest-LINK_ADDRESS+KSEG0_BASE + jal s1 // Branch to routine + ori a2,zero,LED_MEMORY_TEST_1 // set Test/Subtest ID + +// +// Zero the initialized memory +// Data and code are cached. +// + bal PutLedDisplay // call PutLedDisplay to show that + ori a0,zero,0x22 // Mem test is starting + li a0,KSEG0_BASE+0x2000 // base of memory to test non cached. + li a1,0x400000-0x2000 // length to test in bytes + la s1,ZeroMemory-LINK_ADDRESS+KSEG0_BASE + jal s1 // Branch to routine + nop + + +// +// Copy the Basic firmware code to memory. +// + + la s0,DataCopy-LINK_ADDRESS+KSEG0_BASE // address of copy routine in cached space + la a0,end // end of this file=start of Firmware + li a1,RAM_TEST_DESTINATION_ADDRESS // destination is uncached link address. + jal s0 // jump to copy + li a2,0x20000 // size to copy is 128Kb. + nop + bal InvalidateICache // Invalidate the instruction cache + nop + +// +// Initialize the stack to the low memory and Call Rom tests. +// + li t0,RAM_TEST_LINK_ADDRESS // address of copied code + li sp,RAM_TEST_STACK_ADDRESS // init stack + jal t0 // jump to self-test in memory + nop + +// +// Hang blinking 77 if code returns. +// + + li a0,LED_BLINK + bal PutLedDisplay + ori a0,a0,0x77 + + + +// +// The code contained between MemoryRoutines and EndMemoryRoutines +// is copied to memory and executed from there. +// + .align 4 + ALTERNATE_ENTRY(MemoryRoutines) +/*++ +VOID +PutLedDisplay( + a0 - display value. + ) +Routine Description: + + This routine will display in the LED the value specified as argument + a0. + + bits [31:16] specify the mode. + bits [7:4] specify the Test number. + bits [3:0] specify the Subtest number. + + The mode can be: + + LED_NORMAL Display the Test number + LED_BLINK Loop displaying Test - Dot - Subtest + LED_LOOP_ERROR Display the Test number with the dot iluminated + + N.B. This routine must reside in the first page of ROM because it is + called before mapping the rom!! + +Arguments: + + a0 value to display. + + Note: The value of the argument is preserved + +Return Value: + + If a0 set to LED_BLINK does not return. + +--*/ + LEAF_ENTRY(PutLedDisplay) + li t0,DIAGNOSTIC_VIRTUAL_BASE // load address of display +LedBlinkLoop: + srl t1,a0,16 // get upper bits of a0 in t1 + srl t3,a0,4 // get test number + li t4,LED_LOOP_ERROR // + bne t1,t4, DisplayTestID + andi t3,t3,0xF // clear other bits. + ori t3,t3,LED_DECIMAL_POINT // Set decimal point +DisplayTestID: + li t4,LED_BLINK // check if need to hung + sb t3,0(t0) // write test ID to led. + beq t1,t4, ShowSubtestID + nop + j ra // return to caller. + nop + +ShowSubtestID: + li t2,LED_DELAY_LOOP // get delay value. +TestWait: + bne t2,zero,TestWait // loop until zero + addiu t2,t2,-1 // decrement counter + li t3,LED_DECIMAL_POINT+LED_BLANK + sb t3,0(t0) // write decimal point + li t2,LED_DELAY_LOOP/2 // get delay value. +DecPointWait: + bne t2,zero,DecPointWait // loop until zero + addiu t2,t2,-1 // decrement counter + andi t3,a0,0xF // get subtest number + sb t3,0(t0) // write subtest in LED + li t2,LED_DELAY_LOOP // get delay value. +SubTestWait: + bne t2,zero,SubTestWait // loop until zero + addiu t2,t2,-1 // decrement counter + b LedBlinkLoop // go to it again + nop + .end PutLedDisplay + + + + LEAF_ENTRY(InvalidateICache) +/*++ + +Routine Description: + + This routine invalidates the contents of the instruction cache. + + The instruction cache is invalidated by writing an invalid tag to + each cache line, therefore nothing is written back to memory. + +Arguments: + + None. + +Return Value: + + None. + +--*/ +// +// invalid state +// + mfc0 t5,config // read config register + li t0,(PRIMARY_CACHE_INVALID << TAGLO_PSTATE) + mtc0 t0,taglo // set tag registers to invalid + mtc0 zero,taghi + + srl t0,t5,CONFIG_IC // compute instruction cache size + and t0,t0,0x7 // + addu t0,t0,12 // + li t6,1 // + sll t6,t6,t0 // t6 = I cache size + srl t0,t5,CONFIG_IB // compute instruction cache line size + and t0,t0,1 // + li t7,16 // + sll t7,t7,t0 // t7 = I cache line size +// +// store tag to all icache lines +// + li t1,KSEG0_BASE+(1<<20) // get virtual address to index cache + addu t0,t1,t6 // get last index address + subu t0,t0,t7 +WriteICacheTag: + cache INDEX_STORE_TAG_I,0(t1) // store tag in Instruction cache + bne t1,t0,WriteICacheTag // loop + addu t1,t1,t7 // increment index + j ra + nop + .end InvalidateICache + + + LEAF_ENTRY(InvalidateDCache) +/*++ + +Routine Description: + + This routine invalidates the contents of the D cache. + + Data cache is invalidated by writing an invalid tag to each cache + line, therefore nothing is written back to memory. + +Arguments: + + None. + +Return Value: + + None. + +--*/ +// +// invalid state +// + mfc0 t5,config // read config register for cache size + li t0, (PRIMARY_CACHE_INVALID << TAGLO_PSTATE) + mtc0 t0,taglo // set tag to invalid + mtc0 zero,taghi + srl t0,t5,CONFIG_DC // compute data cache size + and t0,t0,0x7 // + addu t0,t0,12 // + li t6,1 // + sll t6,t6,t0 // t6 = data cache size + srl t0,t5,CONFIG_DB // compute data cache line size + and t0,t0,1 // + li t7,16 // + sll t7,t7,t0 // t7 = data cache line size + +// +// store tag to all Dcache +// + li t1,KSEG0_BASE+(1<<20) // get virtual address to index cache + addu t2,t1,t6 // add cache size + subu t2,t2,t7 // adjust for cache line size. +WriteDCacheTag: + cache INDEX_STORE_TAG_D,0(t1) // store tag in Data cache + bne t1,t2,WriteDCacheTag // loop + addu t1,t1,t7 // increment index by cache line + j ra + nop + .end InvalidateDCache + + + LEAF_ENTRY(FlushDCache) +/*++ + +Routine Description: + + This routine flushes the whole contents of the Dcache + +Arguments: + + None. + +Return Value: + + None. + +--*/ + mfc0 t5,config // read config register + li t1,KSEG0_BASE+(1<<20) // get virtual address to index cache + srl t0,t5,CONFIG_DC // compute data cache size + and t0,t0,0x7 // + addu t0,t0,12 // + li t6,1 // + sll t6,t6,t0 // t6 = data cache size + srl t0,t5,CONFIG_DB // compute data cache line size + and t0,t0,1 // + li t7,16 // + sll t7,t7,t0 // t7 = data cache line size + addu t0,t1,t6 // compute last index address + subu t0,t0,t7 +FlushDCacheTag: + cache INDEX_WRITEBACK_INVALIDATE_D,0(t1) // Invalidate data cache + bne t1,t0,FlushDCacheTag // loop + addu t1,t1,t7 // increment index + +// +// check for a secondary cache. +// + + li t1,(1 << CONFIG_SC) + and t0,t5,t1 + bne t0,zero,10f // if non-zero no secondary cache + + li t6,SECONDARY_CACHE_SIZE // t6 = secondary cache size + srl t0,t5,CONFIG_SB // compute secondary cache line size + and t0,t0,3 // + li t7,16 // + sll t7,t7,t0 // t7 = secondary cache line size +// +// invalidate all secondary lines +// + li t1,KSEG0_BASE+(1<<20) // get virtual address to index cache + addu t0,t1,t6 // get last index address + subu t0,t0,t7 +FlushSDCacheTag: + cache INDEX_WRITEBACK_INVALIDATE_SD,0(t1) // invalidate secondary cache + bne t1,t0,FlushSDCacheTag // loop + addu t1,t1,t7 // increment index +10: + j ra + nop + .end FlushDCache + + + LEAF_ENTRY(InvalidateSCache) +/*++ + +Routine Description: + + This routine invalidates the contents of the secondary cache. + + The secondary cache is invalidated by writing an invalid tag to + each cache line, therefore nothing is written back to memory. + +Arguments: + + None. + +Return Value: + + None. + +--*/ + mfc0 t5,config // read config register + +// +// check for a secondary cache. +// + + li t1,(1 << CONFIG_SC) + and t0,t5,t1 + bne t0,zero,NoSecondaryCache // if non-zero no secondary cache + + li t0,(SECONDARY_CACHE_INVALID << TAGLO_SSTATE) + mtc0 t0,taglo // set tag registers to invalid + mtc0 zero,taghi + + li t6,SECONDARY_CACHE_SIZE // t6 = secondary cache size + srl t0,t5,CONFIG_SB // compute secondary cache line size + and t0,t0,3 // + li t7,16 // + sll t7,t7,t0 // t7 = secondary cache line size +// +// store tag to all secondary lines +// + li t1,KSEG0_BASE+(1<<20) // get virtual address to index cache + addu t0,t1,t6 // get last index address + subu t0,t0,t7 +WriteSICacheTag: + cache INDEX_STORE_TAG_SD,0(t1) // store tag in secondary cache + bne t1,t0,WriteSICacheTag // loop + addu t1,t1,t7 // increment index + +NoSecondaryCache: + j ra + nop + .end InvalidateSCache + + + LEAF_ENTRY(InitDataCache) +/*++ + +Routine Description: + + This routine initializes the data fields of the primary and + secondary data caches. + +Arguments: + + None. + +Return Value: + + None. + +--*/ + mfc0 t5,config // read config register + +// +// check for a secondary cache. +// + + li t1,(1 << CONFIG_SC) + and t0,t5,t1 + bne t0,zero,NoSecondaryCache1 // if non-zero no secondary cache + + li t6,SECONDARY_CACHE_SIZE // t6 = secondary cache size + srl t0,t5,CONFIG_SB // compute secondary cache line size + and t0,t0,3 // + li t7,16 // + sll t7,t7,t0 // t7 = secondary cache line size +// +// store tag to all secondary lines +// + li t1,KSEG0_BASE+(1<<20) // get virtual address to index cache + addu t0,t1,t6 // get last index address + subu t0,t0,t7 + +WriteSCacheTag: + cache CREATE_DIRTY_EXCLUSIVE_SD,0(t1) // store tag in secondary cache + bne t1,t0,WriteSCacheTag // loop + addu t1,t1,t7 // increment index + +// +// store data to all secondary lines. 1MB +// + mtc1 zero,f0 // zero f0 + mtc1 zero,f1 // zero f1 + li t1,KSEG0_BASE+(1<<20) // get virtual address to index cache + addu t0,t1,t6 // get last index address + subu t0,t0,64 + +WriteSCacheData: + // + // Init Data 64 bytes per loop. + // + sdc1 f0,0(t1) // write + sdc1 f0,8(t1) // write + sdc1 f0,16(t1) // write + sdc1 f0,24(t1) // write + sdc1 f0,32(t1) // write + sdc1 f0,40(t1) // write + sdc1 f0,48(t1) // write + sdc1 f0,56(t1) // write + bne t1,t0,WriteSCacheData // loop + addu t1,t1,64 // increment index + +// +// Flush the primary data cache to the secondary cache +// + li t1,KSEG0_BASE+(1<<20) // get virtual address to index cache + srl t0,t5,CONFIG_DC // compute data cache size + and t0,t0,0x7 // + addu t0,t0,12 // + li t6,1 // + sll t6,t6,t0 // t6 = data cache size + srl t0,t5,CONFIG_DB // compute data cache line size + and t0,t0,1 // + li t7,16 // + sll t7,t7,t0 // t7 = data cache line size + addu t0,t1,t6 // compute last index address + subu t0,t0,t7 +FlushPDCacheTag: + cache INDEX_WRITEBACK_INVALIDATE_D,0(t1) // Invalidate data cache + bne t1,t0,FlushPDCacheTag // loop + addu t1,t1,t7 // increment index + + j ra // return + nop + +NoSecondaryCache1: + + srl t0,t5,CONFIG_DC // compute data cache size + and t0,t0,0x7 // + addu t0,t0,12 // + li t6,1 // + sll t6,t6,t0 // t6 = data cache size + srl t0,t5,CONFIG_DB // compute data cache line size + and t0,t0,1 // + li t7,16 // + sll t7,t7,t0 // t7 = data cache line size + +// +// create dirty exclusive to all Dcache +// + mtc1 zero,f0 // zero f0 + mtc1 zero,f1 // zero f1 + li t1,KSEG0_BASE+(1<<20) // get virtual address to index cache + addu t2,t1,t6 // add cache size + subu t2,t2,t7 // adjust for cache line size. +WriteDCacheDe: + cache CREATE_DIRTY_EXCLUSIVE_D,0(t1) // store tag in Data cache + nop + sdc1 f0,0(t1) // write + sdc1 f0,8(t1) // write + bne t1,t2,WriteDCacheDe // loop + addu t1,t1,t7 // increment index by cache line + + j ra // return + nop + .end InitDataCache + + LEAF_ENTRY(R4000CacheInit) +/*++ + +Routine Description: + + This routine will initialize the cache tags and data for the + primary data cache, primary instruction cache, and the secondary cache + (if present). + + Subroutines are called to invalidate all of the tags in the + instruction and data caches. + +Arguments: + + None. + +Return Value: + + None. + +--*/ + move s0,ra // save ra. + +// +// Disable Cache Error exceptions. +// + + li t0, (1<<PSR_DE) | (1 << PSR_CU1) | (1 << PSR_BEV) + mtc0 t0,psr + +// +// Invalidate the caches +// + + bal InvalidateICache + nop + + bal InvalidateDCache + nop + + bal InvalidateSCache + nop + +// +// Initialize the data cache(s) +// + + bal InitDataCache + nop + +// +// Fill the Icache, all icache lines +// + + mfc0 t5,config // read config register + nop + srl t0,t5,CONFIG_IC // compute instruction cache size + and t0,t0,0x7 // + addu t0,t0,12 // + li s1,1 // + sll s1,s1,t0 // s1 = I cache size + srl t0,t5,CONFIG_IB // compute instruction cache line size + and t0,t0,1 // + li s2,16 // + sll s2,s2,t0 // s2 = I cache line size + + li t1,KSEG0_BASE+(1<<20) // get virtual address to index cache + addu t0,t1,s1 // add I cache size + subu t0,t0,s2 // sub line size. +FillICache: + cache INDEX_FILL_I,0(t1) // Fill I cache from memory + bne t1,t0,FillICache // loop + addu t1,t1,s2 // increment index + +// +// Invalidate the caches again +// + bal InvalidateICache + nop + + bal InvalidateDCache + nop + + bal InvalidateSCache + nop + +// +// Enable cache error exception. +// + li t1, (1 << PSR_CU1) | (1 << PSR_BEV) + mtc0 t1,psr + nop + nop + nop + move ra,s0 // move return address back to ra + j ra // return from routine + nop + .end R4000CacheInit + + + +/*++ +VOID +MemoryTest( + StartAddress + Size + ) +Routine Description: + + This routine will test the supplied range of memory by + First writing the address of each location into each location. + Second writing the address with all bits flipped into each location + + This ensures that all bits are toggled. + +Arguments: + + a0 - supplies start of memory area to test + a1 - supplies length of memory area in bytes + + Note: the values of the arguments are preserved. + +Return Value: + + This routine returns no value. +--*/ + LEAF_ENTRY(MemoryTest) + +// +// Write first pattern +// + add t1,a0,a1 // t1 = last address. + addiu t1,t1,-4 // adjust for end condition + move t2,a0 // t2=current address +FirstWrite: + sw t2,0(t2) // store first address + addiu t2,t2,4 // compute next address + sw t2,0(t2) // store first address + addiu t2,t2,4 // compute next address + sw t2,0(t2) // store first address + addiu t2,t2,4 // compute next address + sw t2,0(t2) // store + bne t2,t1,FirstWrite // check for end condition + addiu t2,t2,4 // compute next address + +// +// Check first pattern +// + + addiu t3,a0,-4 // t3 first address-4 + add t2,a0,a1 // last address. + addiu t2,t2,-8 // adjust + move t1,t3 // get copy of t3 just for first check +FirstCheck: + bne t1,t3,PatternFail + lw t1,4(t3) // load from first location + addiu t3,t3,4 // compute next address + bne t1,t3,PatternFail + lw t1,4(t3) // load from next location + addiu t3,t3,4 // compute next address + bne t1,t3,PatternFail + lw t1,4(t3) // load from next location + addiu t3,t3,4 // compute next address + bne t1,t3,PatternFail // check + lw t1,4(t3) // load from next location + bne t3,t2,FirstCheck // check for end condition + addiu t3,t3,4 // compute next address + bne t1,t3,PatternFail // check last + +// +// Write second pattern +// + + li a3,0xFFFFFFFF // XOR value to flip all bits. + add t1,a0,a1 // t1 = last address. + xor t0,a0,a3 // t0 value to write + move t2,a0 // t2=current address +SecondWrite: + sw t0,0(t2) // store + addiu t2,t2,4 // compute next address + xor t0,t2,a3 // next pattern + sw t0,0(t2) + addiu t2,t2,4 // compute next address + xor t0,t2,a3 // next pattern + sw t0,0(t2) + addiu t2,t2,4 // compute next address + xor t0,t2,a3 // next pattern + sw t0,0(t2) + addiu t2,t2,4 // compute next address + bne t2,t1,SecondWrite // check for end condition + xor t0,t2,a3 // value to write +// +// Check second pattern +// + move t3,a0 // t3 first address. + add t2,a0,a1 // last address. +SecondCheck: + lw t1,0(t3) // load from first location + xor t0,t3,a3 // first expected value + bne t1,t0,PatternFail + addiu t3,t3,4 // compute next address + lw t1,0(t3) // load from first location + xor t0,t3,a3 // first expected value + bne t1,t0,PatternFail + addiu t3,t3,4 // compute next address + lw t1,0(t3) // load from first location + xor t0,t3,a3 // first expected value + bne t1,t0,PatternFail + addiu t3,t3,4 // compute next address + lw t1,0(t3) // load from first location + xor t0,t3,a3 // first expected value + bne t1,t0,PatternFail // check last one. + addiu t3,t3,4 // compute next address + bne t3,t2,SecondCheck // check for end condition + nop + j ra + nop + +PatternFail: + // + // check if we are in loop on error + // + li t0,DIAGNOSTIC_VIRTUAL_BASE // get base address of diag register + lbu t0,0(t0) // read register value. + li t1,LOOP_ON_ERROR_MASK // get value to compare + andi t0,DIAGNOSTIC_MASK // mask diagnostic bits. + li v0,PROM_ENTRY(14) // load address of PutLedDisplay + beq t1,t0,10f // branch if loop on error. + move s8,a0 // save register a0 + lui t0,LED_BLINK // get LED blink code + jal v0 // Blink LED and hang. + or a0,a2,t0 // pass a2 as argument in a0 +10: + lui t0,LED_LOOP_ERROR // get LED LOOP_ERROR code + jal v0 // Set LOOP ON ERROR on LED + or a0,a2,t0 // pass a2 as argument in a0 + b MemoryTest // Loop back to test again. + move a0,s8 // restoring arguments. + + .end MemoryTest + +/*++ +VOID +ZeroMemory( + ULONG StartAddress + ULONG Size + ); +Routine Description: + + This routine will zero a range of memory. + +Arguments: + + a0 - supplies start of memory + a1 - supplies length of memory in bytes + +Return Value: + + None. + +--*/ + LEAF_ENTRY(ZeroMemory) + mtc1 zero,f0 // zero cop1 f0 register + mtc1 zero,f1 // zero cop1 f1 register + add a1,a1,a0 // Compute End address + addiu a1,a1,-16 // adjust address +ZeroMemoryLoop: + sdc1 f0,0(a0) // zero memory. + sdc1 f0,8(a0) // zero memory. + bne a0,a1,ZeroMemoryLoop // loop until done. + addiu a0,a0,16 // compute next address + j ra // return + nop + .end ZeroMemory + +/*++ +VOID +DataCopy( + ULONG SourceAddress + ULONG DestinationAddress + ULONG Length + ); +Routine Description: + + This routine will copy data from one location to another + Source, destination, and length must be dword aligned. + + For DUO since 64 bit reads from the prom are not supported, the + reads and writes are done word by word. + +Arguments: + + a0 - supplies source of data + a1 - supplies destination of data + a2 - supplies length of data in bytes + +Return Value: + + None. +--*/ + + + LEAF_ENTRY(DataCopy) + +#ifndef DUO + + add a2,a2,a0 // get last address +CopyLoop: + ldc1 f0,0(a0) // load 1st double word + ldc1 f2,8(a0) // load 2nd double word + addiu a0,a0,16 // increment source pointer + sdc1 f0,0(a1) // store 1st double word + sdc1 f2,8(a1) // store 2nd double word + bne a0,a2,CopyLoop // loop until address=last address + addiu a1,a1,16 // increment destination pointer + j ra // return + nop +#else + + add a2,a2,a0 // get last address +CopyLoop: + lw t0, 0(a0) // load 1st word + lw t1, 4(a0) // load 2nd word + lw t2, 8(a0) // load 3rd word + lw t3,12(a0) // load 4th word + addiu a0,a0,16 // increment source pointer + sw t0, 0(a1) // load 1st word + sw t1, 4(a1) // load 2nd word + sw t2, 8(a1) // load 3rd word + sw t3,12(a1) // load 4th word + bne a0,a2,CopyLoop // loop until address=last address + addiu a1,a1,16 // increment destination pointer + j ra // return + nop +#endif + .align 4 // Align it to 16 bytes boundary so that + ALTERNATE_ENTRY(EndMemoryRoutines) // DataCopy doesn't need to check alignments + nop + .end DataCopy + + + +// +// SendKbdData: +// +// Routine Description: +// +// This routine polls the keyboard status register until the controller +// is ready to accept a command or timeout. Then it sends the data. +// +// Arguments: +// +// a0 - Data value. +// +// Return Value: +// +// TRUE if timeout +// FALSE if OK. +// +// + LEAF_ENTRY(SendKbdData) + li t0,KBD_TIMEOUT*2 // Init timeout counter + li v1,KEYBOARD_VIRTUAL_BASE // Load Base of Kbd controller +10: + lbu t1,KbdStatusReg(v1) // read Status port. + andi t1,t1,KBD_IBF_MASK // Test input buffer full bit + beq t1,zero,20f // if Not full go to write data + addiu t0,t0,-1 // decrement timeout counter. + bne t0,zero,10b // Loop if not timeout + li v0,1 // set return value to TRUE + j ra // return to caller + nop +20: sb a0,KbdDataReg(v1) // write data byte to kbd controller + j ra // return to caller + move v0,zero // Set return value to FALSE + + .end SendKbdData + +// +// SendKbdCommand: +// +// Routine Description: +// +// This routine polls the keyboard status register until the controller +// is ready to accept a command or timeout. Then it sends the command. +// +// Arguments: +// +// a0 - Command value. +// +// Return Value: +// +// TRUE if timeout +// FALSE if OK. +// +// + LEAF_ENTRY(SendKbdCommand) + + li t0,KBD_TIMEOUT*2 // Init timeout counter + li v1,KEYBOARD_VIRTUAL_BASE // Load Base of Kbd controller +10: + lbu t1,KbdStatusReg(v1) // read Status port. + andi t1,t1,KBD_IBF_MASK // Test input buffer full bit + beq t1,zero,20f // if Not full go write command + addiu t0,t0,-1 // decrement timeout counter. + bne t0,zero,10b // Loop if not timeout + li v0,1 // set return value to TRUE + j ra // return to caller + nop +20: sb a0,KbdCommandReg(v1) // write command to kbd controller + j ra // return to caller + move v0,zero // Set return value to FALSE + + .end SendKbdCommand + +// +// ClearKbdFifo: +// +// Routine Description: +// +// This routine empties the keyboard controller fifo. +// +// Arguments: +// +// None +// +// Return Value: +// +// None. +// +// + LEAF_ENTRY(ClearKbdFifo) + + li v1,KEYBOARD_VIRTUAL_BASE // Load Base of Kbd controller +10: + lbu t1,KbdStatusReg(v1) // read Status port. + andi t1,t1,KBD_IBF_MASK // Test input buffer full bit + bne t1,zero,10b // Not zero = full Keep Looping. + nop + + li t0,2000 // Init wait counter +Wait: + bne t0,zero,Wait // wait until counter reaches zero + addiu t0,t0,-1 // decrement counter + +20: + lbu t1,KbdStatusReg(v1) // read Status port. + andi t1,t1,KBD_OBF_MASK // Test Output buffer full bit + beq t1,zero,40f // if zero Output buffer empty. + li t0,2000 // Init wait counter + lbu zero,KbdDataReg(v1) // read Data port. +30: + bne t0,zero,30b // wait until counter reaches zero + addiu t0,t0,-1 // decrement counter + b 20b // go to check if more bytes in fifo. + nop +40: + j ra // return to caller + nop + .end ClearKbdFifo + + + +// +// BOOL +// UCHAR +// GetKbdData: +// +// Routine Description: +// +// This routine polls the Status Register until Data is available or timeout, +// then it reads and returns the Data. +// +// Arguments: +// +// a0 - Timeout value in milliseconds. +// +// Return Value: +// +// Returns the data byte read from the keyboard controller in v1. +// TRUE if timeout FALSE otherwise in v0 +// +// + + + LEAF_ENTRY(GetKbdData) + + li v1,KEYBOARD_VIRTUAL_BASE // Load Base of Kbd controller +// +// scale timeout value in millisexonds to loop counter. +// 6 instructions per loop * 2us/instruction * 128 = 1ms. +// + sll a0,8 +10: + lbu t1,KbdStatusReg(v1) // read Status port. + andi t1,t1,KBD_OBF_MASK // Test output buffer full bit + bne t1,zero,20f // if full go read data + addiu a0,a0,-1 // decrement timeout counter. + bne a0,zero,10b // Loop if not timeout + li v0,1 // set return value to TRUE + j ra // return to caller + nop +20: lbu v1,KbdDataReg(v1) // read data byte from kbd controller + j ra // return to caller + move v0,zero // return false + + .end GetKbdData + + + +// +// InitKeyboardController: +// +// Routine Description: +// +// This routine initializes the keyboard controller. +// +// Arguments: +// +// None +// +// Return Value: +// +// TRUE if timeout +// FALSE if OK. +// +// + LEAF_ENTRY(InitKeyboardController) + + .set reorder + .set at + move s0,ra // save return adr + bal ClearKbdFifo // clear both fifos. + li a0,KBD_CTR_SELFTEST // Send selftest command + bal SendKbdCommand // to the keyboard ctr. + bne v0,zero,10f // if return TRUE an error occurred + li a0,1000 // Timeout value in ms + bal GetKbdData // Read data byte from kbd + bne v0,zero,10f // if return TRUE an error occurred + li t0,Kbd_Ctr_Selftest_Passed // get expected data byte. + bne t0,v1,10f // Fail if data read from ctrlr not expected. + + // + // Test Kbd lines. + // + + li a0,KBD_CTR_KBDLINES_TEST // Send kbd lines test command + bal SendKbdCommand // to the keyboard ctr. + bne v0,zero,10f // if return TRUE an error occurred + li a0,1000 // Timeout value in ms + bal GetKbdData // Read data byte from kbd + bne v0,zero,10f // if return TRUE an error occurred + li t0,INTERFACE_NO_ERROR // get expected data byte. + bne t0,v1,10f // Fail if data read from ctrlr not expected. + + // + // Test Aux lines. + // + + li a0,KBD_CTR_AUXLINES_TEST // Send aux lines test command + bal SendKbdCommand // to the keyboard ctr. + bne v0,zero,10f // if return TRUE an error occurred + li a0,1000 // Timeout value in ms + bal GetKbdData // Read data byte from kbd + bne v0,zero,10f // if return TRUE an error occurred + li t0,INTERFACE_NO_ERROR // get expected data byte. + bne t0,v1,10f // Fail if data read from ctrlr not expected. + +// +// Send Reset to Keyboard. +// + + bal ClearKbdFifo // Clear Kbd fifo again. +ResendLoop: + li a0,KbdReset // Send keyboard reset + bal SendKbdData // to the keyboard controller. + bne v0,zero,10f // if return TRUE an error occurred + li a0,1000 // Timeout value in ms + bal GetKbdData // Read data byte from kbd + bne v0,zero,10f // if return TRUE an error occurred + li t0,KbdResend // get resend value + bne t0,v1,5f // If not resend continue. + // + // Resend received. + // Get another Data byte and resend Reset command. + // + + li a0,1000 // Timeout value in ms + bal GetKbdData // Read data byte from kbd + bne v0,zero,10f // if return TRUE an error occurred + b ResendLoop + +5: + li t0,KbdAck // get expected value + bne t0,v1,10f + + li a0,7000 // Timeout value in ms + bal GetKbdData // Read data byte from kbd + bne v0,zero,10f // if return TRUE an error occurred + li t0,KbdBat // get expected value + bne t0,v1,10f + +// +// Enable Kbd and Select keyboard Scan code. +// + li a0,KBD_CTR_ENABLE_KBD // Enable Keyboard cmd + bal SendKbdCommand // send to kbd ctrl. + bne v0,zero,10f // if return TRUE an error occurred + li a0,KbdSelScanCode // Select scan code. + bal SendKbdData // send to kbd + bne v0,zero,10f // if return TRUE an error occurred + li a0,1000 // Timeout value in ms + bal GetKbdData // Read data byte from kbd + bne v0,zero,10f // if return TRUE an error occurred + li a0,1 // select Scan code 1 + bal SendKbdData + bne v0,zero,10f // if return TRUE an error occurred + li a0,1000 // Timeout value in ms + bal GetKbdData // Read data byte from kbd + bne v0,zero,10f // if return TRUE an error occurred +// +// Test Successfull. Return FALSE to caller. +// + move v0,zero + j s0 + + +10: + +// +// Test Unsuccessfull. Return TRUE to caller. +// + li v0,1 + j s0 + + .set noreorder + .set noat + + .end InitKeyboardController + + +#if 0 +/*++ +VOID +LedDisplayNumber( + a0 - 32 bit value to display + ) +Routine Description: + +Arguments: + + a0 value to display. + + Note: The value of the argument is preserved + +Return Value: + +--*/ + LEAF_ENTRY(LedDisplayNumber) + li t0,DIAGNOSTIC_VIRTUAL_BASE // load address of display + + + li t1,LED_DECIMAL_POINT | 0xE // Display .E + sb t1,0(t0) // + li t2,LED_DELAY_LOOP*2 // get delay value. +10: + bne t2,zero,10b // loop until zero + addiu t2,t2,-1 // decrement counter + + li t7,8 // 8 digits + li t6,28 // shift amount +DigitLoop: + srl t1,a0,t6 // shift + nop + andi t1,0xF // get lower nibble + sb t1,0(t0) // display digit + + li t2,LED_DELAY_LOOP*2 // get delay value. +10: + bne t2,zero,10b // loop until zero + addiu t2,t2,-1 // decrement counter + + li t1,LED_DECIMAL_POINT | LED_BLANK // Display . between digits + sb t1,0(t0) // + li t2,LED_DELAY_LOOP // get delay value. +10: + bne t2,zero,10b // loop until zero + addiu t2,t2,-1 // decrement counter + + + addiu t7,t7,-1 // decrement Main loop counter + bne t7,zero,DigitLoop + addiu t6,t6,-4 + + li t2,LED_DELAY_LOOP*2 // get delay value. +10: + bne t2,zero,10b // loop until zero + addiu t2,t2,-1 // decrement counter + + + + b LedDisplayNumber + nop + + .end LedDisplayNumber + + +#endif +#endif // DUO && R4000 diff --git a/private/ntos/fw/duobase/mips/fatboot.c b/private/ntos/fw/duobase/mips/fatboot.c new file mode 100644 index 000000000..a4facb64d --- /dev/null +++ b/private/ntos/fw/duobase/mips/fatboot.c @@ -0,0 +1,2307 @@ +/*++ + +Copyright (c) 1991 Microsoft Corporation + +Module Name: + + fatboot.c + +Abstract: + + This module implements the FAT boot file system used by the operating + system loader. + +Author: + + Gary Kimura (garyki) 29-Aug-1989 + +Revision History: + +--*/ + +#include "fwp.h" +#include "stdio.h" + +// +// Conditional debug print routine +// + +#ifdef FATBOOTDBG + +#define FatDebugOutput(X,Y,Z) { \ + if (BlConsoleOutDeviceId) { \ + CHAR _b[128]; \ + ULONG _c; \ + sprintf(&_b[0], X, Y, Z); \ + ArcWrite(BlConsoleOutDeviceId, &_b[0], strlen(&_b[0]), &_c); \ + } \ +} + +#else + +#define FatDebugOutput(X,Y,Z) {NOTHING;} + +#endif // FATBOOTDBG + + +// +// Low level disk I/O procedure prototypes +// + +ARC_STATUS +FatDiskRead ( + IN ULONG DeviceId, + IN LBO Lbo, + IN ULONG ByteCount, + IN PVOID Buffer + ); + +#define DiskRead(A,B,C,D) { ARC_STATUS _s; \ + if ((_s = FatDiskRead(A,B,C,D)) != ESUCCESS) { return _s; } \ +} + + +// +// Cluster/Index routines +// + +typedef enum _CLUSTER_TYPE { + FatClusterAvailable, + FatClusterReserved, + FatClusterBad, + FatClusterLast, + FatClusterNext +} CLUSTER_TYPE; + +CLUSTER_TYPE +FatInterpretClusterType ( + IN PFAT_STRUCTURE_CONTEXT FatStructureContext, + IN FAT_ENTRY Entry + ); + +ARC_STATUS +FatLookupFatEntry ( + IN PFAT_STRUCTURE_CONTEXT FatStructureContext, + IN ULONG DeviceId, + IN FAT_ENTRY FatIndex, + OUT PFAT_ENTRY FatEntry + ); + + +LBO +FatIndexToLbo ( + IN PFAT_STRUCTURE_CONTEXT FatStructureContext, + IN FAT_ENTRY FatIndex + ); + +VOID +FatLboToIndex ( + IN PFAT_STRUCTURE_CONTEXT FatStructureContext, + IN LBO Lbo, + OUT PFAT_ENTRY FatIndex, + OUT PULONG ByteOffset + ); + +#define LookupFatEntry(A,B,C,D) { ARC_STATUS _s; \ + if ((_s = FatLookupFatEntry(A,B,C,D)) != ESUCCESS) { return _s; } \ +} + + + +// +// Directory routines +// + +ARC_STATUS +FatSearchForDirent ( + IN PFAT_STRUCTURE_CONTEXT FatStructureContext, + IN ULONG DeviceId, + IN FAT_ENTRY DirectoriesStartingIndex, + IN PFAT8DOT3 FileName, + OUT PDIRENT Dirent, + OUT PLBO Lbo + ); + +ARC_STATUS +FatCreateDirent ( + IN PFAT_STRUCTURE_CONTEXT FatStructureContext, + IN ULONG DeviceId, + IN FAT_ENTRY DirectoriesStartingIndex, + IN PDIRENT Dirent, + OUT PLBO Lbo + ); + +VOID +FatSetDirent ( + IN PFAT8DOT3 FileName, + IN OUT PDIRENT Dirent, + IN UCHAR Attributes + ); + +#define SearchForDirent(A,B,C,D,E,F) { ARC_STATUS _s; \ + if ((_s = FatSearchForDirent(A,B,C,D,E,F)) != ESUCCESS) { return _s; } \ +} + +#define CreateDirent(A,B,C,D,E) { ARC_STATUS _s; \ + if ((_s = FatCreateDirent(A,B,C,D,E)) != ESUCCESS) { return _s; } \ +} + + +// +// Allocation and mcb routines +// + +ARC_STATUS +FatLoadMcb ( + IN ULONG FileId, + IN VBO StartingVbo + ); + +ARC_STATUS +FatVboToLbo ( + IN ULONG FileId, + IN VBO Vbo, + OUT PLBO Lbo, + OUT PULONG ByteCount + ); + +ARC_STATUS +FatIncreaseFileAllocation ( + IN ULONG FileId, + IN ULONG ByteSize + ); + +ARC_STATUS +FatAllocateClusters ( + IN PFAT_STRUCTURE_CONTEXT FatStructureContext, + IN ULONG DeviceId, + IN ULONG ClusterCount, + IN FAT_ENTRY Hint, + OUT PFAT_ENTRY AllocatedEntry + ); + +#define LoadMcb(A,B) { ARC_STATUS _s; \ + if ((_s = FatLoadMcb(A,B)) != ESUCCESS) { return _s; } \ +} + +#define VboToLbo(A,B,C,D) { ARC_STATUS _s; \ + if ((_s = FatVboToLbo(A,B,C,D)) != ESUCCESS) { return _s; } \ +} + +#define IncreaseFileAllocation(A,B) { ARC_STATUS _s; \ + if ((_s = FatIncreaseFileAllocation(A,B)) != ESUCCESS) { return _s; } \ +} + +#define AllocateClusters(A,B,C,D,E) { ARC_STATUS _s; \ + if ((_s = FatAllocateClusters(A,B,C,D,E)) != ESUCCESS) { return _s; } \ +} + + +// +// Miscellaneous routines +// + +VOID +FatFirstComponent ( + IN OUT PSTRING String, + OUT PFAT8DOT3 FirstComponent + ); + +#define AreNamesEqual(X,Y) ( \ + ((*(X))[0]==(*(Y))[0]) && ((*(X))[1]==(*(Y))[1]) && ((*(X))[2]==(*(Y))[2]) && \ + ((*(X))[3]==(*(Y))[3]) && ((*(X))[4]==(*(Y))[4]) && ((*(X))[5]==(*(Y))[5]) && \ + ((*(X))[6]==(*(Y))[6]) && ((*(X))[7]==(*(Y))[7]) && ((*(X))[8]==(*(Y))[8]) && \ + ((*(X))[9]==(*(Y))[9]) && ((*(X))[10]==(*(Y))[10]) ? TRUE : FALSE \ +) + +#define ToUpper(C) ((((C) >= 'a') && ((C) <= 'z')) ? (C) - 'a' + 'A' : (C)) + +#define FlagOn(Flags,SingleFlag) ((BOOLEAN)(((Flags) & (SingleFlag)) != 0 ? TRUE : FALSE)) +#define SetFlag(Flags,SingleFlag) { (Flags) |= (SingleFlag); } +#define ClearFlag(Flags,SingleFlag) { (Flags) &= ~(SingleFlag); } + +#define FatFirstFatAreaLbo(B) ( (B)->ReservedSectors * (B)->BytesPerSector ) + +#define Minimum(X,Y) ((X) < (Y) ? (X) : (Y)) + +// +// The following types and macros are used to help unpack the packed and +// misaligned fields found in the Bios parameter block +// + +typedef union _UCHAR1 { UCHAR Uchar[1]; UCHAR ForceAlignment; } UCHAR1, *PUCHAR1; +typedef union _UCHAR2 { UCHAR Uchar[2]; USHORT ForceAlignment; } UCHAR2, *PUCHAR2; +typedef union _UCHAR4 { UCHAR Uchar[4]; ULONG ForceAlignment; } UCHAR4, *PUCHAR4; + +#define CopyUchar1(Dst,Src) { \ + ((PUCHAR1)(Dst))->Uchar[0] = ((PUCHAR1)(Src))->Uchar[0]; \ + } + +#define CopyUchar2(Dst,Src) { \ + ((PUCHAR2)(Dst))->Uchar[0] = ((PUCHAR2)(Src))->Uchar[0]; \ + ((PUCHAR2)(Dst))->Uchar[1] = ((PUCHAR2)(Src))->Uchar[1]; \ + } + +#define CopyUchar4(Dst,Src) { \ + ((PUCHAR4)(Dst))->Uchar[0] = ((PUCHAR4)(Src))->Uchar[0]; \ + ((PUCHAR4)(Dst))->Uchar[1] = ((PUCHAR4)(Src))->Uchar[1]; \ + ((PUCHAR4)(Dst))->Uchar[2] = ((PUCHAR4)(Src))->Uchar[2]; \ + ((PUCHAR4)(Dst))->Uchar[3] = ((PUCHAR4)(Src))->Uchar[3]; \ + } + +// +// DirectoryEntry routines +// + +VOID +FatDirToArcDir + ( + IN PDIRENT FatDirEnt, + OUT PDIRECTORY_ENTRY ArcDirEnt + ); + +VOID +FatNameToArcName + ( + IN FAT8DOT3 FatName, + OUT PCHAR ArcName, + OUT PULONG ArcNameLength + ); + + +// +// Define global data. +// + +// +// File entry table - This is a structure that provides entry to the FAT +// file system procedures. It is exported when a FAT file structure +// is recognized. +// + +BL_DEVICE_ENTRY_TABLE FatDeviceEntryTable; + + +PBL_DEVICE_ENTRY_TABLE +IsFatFileStructure ( + IN ULONG DeviceId, + IN PVOID StructureContext + ) + +/*++ + +Routine Description: + + This routine determines if the partition on the specified channel + contains a FAT file system volume. + +Arguments: + + DeviceId - Supplies the file table index for the device on which + read operations are to be performed. + + StructureContext - Supplies a pointer to a FAT file structure context. + +Return Value: + + A pointer to the FAT entry table is returned if the partition is + recognized as containing a FAT volume. Otherwise, NULL is returned. + +--*/ + +{ + PPACKED_BOOT_SECTOR BootSector; + UCHAR Buffer[sizeof(PACKED_BOOT_SECTOR)+256]; + + PFAT_STRUCTURE_CONTEXT FatStructureContext; + + FatDebugOutput("IsFatFileStructure\r\n", 0, 0); + + // + // Clear the file system context block for the specified channel and + // establish a pointer to the context structure that can be used by other + // routines + // + + FatStructureContext = (PFAT_STRUCTURE_CONTEXT)StructureContext; + RtlZeroMemory(FatStructureContext, sizeof(FAT_STRUCTURE_CONTEXT)); + + // + // Setup and read in the boot sector for the potential fat partition + // + + BootSector = (PPACKED_BOOT_SECTOR)ALIGN_BUFFER( &Buffer[0] ); + + if (FatDiskRead(DeviceId, 0, sizeof(PACKED_BOOT_SECTOR), BootSector) != ESUCCESS) { + + return NULL; + } + + // + // Unpack the Bios parameter block + // + + FatUnpackBios(&FatStructureContext->Bpb, &BootSector->PackedBpb); + + // + // Check if it is fat + // + + if ((BootSector->Jump[0] != 0xeb) && + (BootSector->Jump[0] != 0xe9)) { + + return NULL; + + } else if ((FatStructureContext->Bpb.BytesPerSector != 128) && + (FatStructureContext->Bpb.BytesPerSector != 256) && + (FatStructureContext->Bpb.BytesPerSector != 512) && + (FatStructureContext->Bpb.BytesPerSector != 1024)) { + + return NULL; + + } else if ((FatStructureContext->Bpb.SectorsPerCluster != 1) && + (FatStructureContext->Bpb.SectorsPerCluster != 2) && + (FatStructureContext->Bpb.SectorsPerCluster != 4) && + (FatStructureContext->Bpb.SectorsPerCluster != 8) && + (FatStructureContext->Bpb.SectorsPerCluster != 16) && + (FatStructureContext->Bpb.SectorsPerCluster != 32) && + (FatStructureContext->Bpb.SectorsPerCluster != 64) && + (FatStructureContext->Bpb.SectorsPerCluster != 128)) { + + return NULL; + + } else if (FatStructureContext->Bpb.ReservedSectors == 0) { + + return NULL; + + } else if (FatStructureContext->Bpb.Fats == 0) { + + return NULL; + + } else if (FatStructureContext->Bpb.RootEntries == 0) { + + return NULL; + + } else if (((FatStructureContext->Bpb.Sectors == 0) && (FatStructureContext->Bpb.LargeSectors == 0)) || + ((FatStructureContext->Bpb.Sectors != 0) && (FatStructureContext->Bpb.LargeSectors != 0))) { + + return NULL; + + } else if (FatStructureContext->Bpb.SectorsPerFat == 0) { + + return NULL; + + } else if ((FatStructureContext->Bpb.Media != 0xf0) && + (FatStructureContext->Bpb.Media != 0xf8) && + (FatStructureContext->Bpb.Media != 0xf9) && + (FatStructureContext->Bpb.Media != 0xfc) && + (FatStructureContext->Bpb.Media != 0xfd) && + (FatStructureContext->Bpb.Media != 0xfe) && + (FatStructureContext->Bpb.Media != 0xff)) { + + return NULL; + } + + // + // Initialize the file entry table and return the address of the table. + // + + FatDeviceEntryTable.Open = FatOpen; + FatDeviceEntryTable.Close = FatClose; + FatDeviceEntryTable.Read = FatRead; + FatDeviceEntryTable.Seek = FatSeek; + FatDeviceEntryTable.Write = NULL; + FatDeviceEntryTable.GetFileInformation = FatGetFileInformation; + FatDeviceEntryTable.SetFileInformation = NULL; + FatDeviceEntryTable.Rename = NULL; ; + FatDeviceEntryTable.GetDirectoryEntry = FatGetDirectoryEntry; + + return &FatDeviceEntryTable; +} + +ARC_STATUS +FatClose ( + IN ULONG FileId + ) + +/*++ + +Routine Description: + + This routine closes the file specified by the file id. + +Arguments: + + FileId - Supplies the file table index. + +Return Value: + + ESUCCESS if returned as the function value. + +--*/ + +{ + PBL_FILE_TABLE FileTableEntry; + PFAT_STRUCTURE_CONTEXT FatStructureContext; + ULONG DeviceId; + + FatDebugOutput("FatClose\r\n", 0, 0); + + // + // Load our local variables + // + + FileTableEntry = &BlFileTable[FileId]; + FatStructureContext = (PFAT_STRUCTURE_CONTEXT)FileTableEntry->StructureContext; + DeviceId = FileTableEntry->DeviceId; + + // + // Mark the file closed + // + + BlFileTable[FileId].Flags.Open = 0; + + // + // Check if the current mcb is for this file and if it is then zero it out. + // By setting the file id for the mcb to be the table size we guarantee that + // we've just set it to an invalid file id. + // + + if (FatStructureContext->FileId == FileId) { + + FatStructureContext->FileId = BL_FILE_TABLE_SIZE; + FatStructureContext->Mcb.InUse = 0; + } + + return ESUCCESS; +} + + +ARC_STATUS +FatGetFileInformation ( + IN ULONG FileId, + OUT PFILE_INFORMATION Buffer + ) + +/*++ + +Routine Description: + + This procedure returns to the user a buffer filled with file information + +Arguments: + + FileId - Supplies the File id for the operation + + Buffer - Supplies the buffer to receive the file information. Note that + it must be large enough to hold the full file name + +Return Value: + + ESUCCESS is returned if the open operation is successful. Otherwise, + an unsuccessful status is returned that describes the reason for failure. + +--*/ + +{ + PBL_FILE_TABLE FileTableEntry; + UCHAR Attributes; + ULONG i; + + FatDebugOutput("FatGetFileInformation\r\n", 0, 0); + + // + // Load our local variables + // + + FileTableEntry = &BlFileTable[FileId]; + Attributes = FileTableEntry->u.FatFileContext.Dirent.Attributes; + + // + // Zero out the buffer, and fill in its non-zero values. + // + + RtlZeroMemory(Buffer, sizeof(FILE_INFORMATION)); + + Buffer->EndingAddress.LowPart = FileTableEntry->u.FatFileContext.Dirent.FileSize; + + Buffer->CurrentPosition.LowPart = FileTableEntry->Position.LowPart; + Buffer->CurrentPosition.HighPart = 0; + + if (FlagOn(Attributes, FAT_DIRENT_ATTR_READ_ONLY)) { SetFlag(Buffer->Attributes, ArcReadOnlyFile) }; + if (FlagOn(Attributes, FAT_DIRENT_ATTR_HIDDEN)) { SetFlag(Buffer->Attributes, ArcHiddenFile) }; + if (FlagOn(Attributes, FAT_DIRENT_ATTR_SYSTEM)) { SetFlag(Buffer->Attributes, ArcSystemFile) }; + if (FlagOn(Attributes, FAT_DIRENT_ATTR_ARCHIVE)) { SetFlag(Buffer->Attributes, ArcArchiveFile) }; + if (FlagOn(Attributes, FAT_DIRENT_ATTR_DIRECTORY)) { SetFlag(Buffer->Attributes, ArcDirectoryFile) }; + + Buffer->FileNameLength = FileTableEntry->FileNameLength; + + for (i = 0; i < FileTableEntry->FileNameLength; i += 1) { + + Buffer->FileName[i] = FileTableEntry->FileName[i]; + } + + return ESUCCESS; +} + + +ARC_STATUS +FatOpen ( + IN PCHAR FileName, + IN OPEN_MODE OpenMode, + IN PULONG FileId + ) + +/*++ + +Routine Description: + + This routine searches the device for a file matching FileName. + If a match is found the dirent for the file is saved and the file is + opened. + +Arguments: + + FileName - Supplies a pointer to a zero terminated file name. + + OpenMode - Supplies the mode of the open. + + FileId - Supplies a pointer to a variable that specifies the file + table entry that is to be filled in if the open is successful. + +Return Value: + + ESUCCESS is returned if the open operation is successful. Otherwise, + an unsuccessful status is returned that describes the reason for failure. + +--*/ + +{ + PBL_FILE_TABLE FileTableEntry; + PFAT_STRUCTURE_CONTEXT FatStructureContext; + ULONG DeviceId; + + FAT_ENTRY CurrentDirectoryIndex; + BOOLEAN SearchSucceeded; + BOOLEAN IsDirectory; + BOOLEAN IsReadOnly; + + STRING PathName; + FAT8DOT3 Name; + + FatDebugOutput("FatOpen\r\n", 0, 0); + + // + // Load our local variables + // + + FileTableEntry = &BlFileTable[*FileId]; + FatStructureContext = (PFAT_STRUCTURE_CONTEXT)FileTableEntry->StructureContext; + DeviceId = FileTableEntry->DeviceId; + + // + // Construct a file name descriptor from the input file name + // + + RtlInitString( &PathName, FileName ); + + // + // While the path name has some characters in it we'll go through our loop + // which extracts the first part of the path name and searches the current + // directory for an entry. If what we find is a directory then we have to + // continue looping until we're done with the path name. + // + + FileTableEntry->u.FatFileContext.DirentLbo = 0; + FileTableEntry->Position.LowPart = 0; + FileTableEntry->Position.HighPart = 0; + + CurrentDirectoryIndex = 0; + SearchSucceeded = TRUE; + IsDirectory = TRUE; + IsReadOnly = TRUE; + + if ((PathName.Buffer[0] == '\\') && (PathName.Length == 1)) { + + // + // We are opening the root directory. + // + // N.B.: IsDirectory and SearchSucceeded are already TRUE. + // + + PathName.Length = 0; + + FileTableEntry->FileNameLength = 1; + FileTableEntry->FileName[0] = PathName.Buffer[0]; + + // + // Root dirent is all zeroes with a directory attribute. + // + + RtlZeroMemory(&FileTableEntry->u.FatFileContext.Dirent, sizeof(DIRENT)); + + FileTableEntry->u.FatFileContext.Dirent.Attributes = FAT_DIRENT_ATTR_DIRECTORY; + + FileTableEntry->u.FatFileContext.DirentLbo = 0; + + IsReadOnly = FALSE; + + CurrentDirectoryIndex = FileTableEntry->u.FatFileContext.Dirent.FirstClusterOfFile; + + } else { + + // + // We are not opening the root directory. + // + + while ((PathName.Length > 0) && IsDirectory) { + + ARC_STATUS Status; + + // + // Extract the first component and search the directory for a match, but + // first copy the first part to the file name buffer in the file table entry + // + + if (PathName.Buffer[0] == '\\') { + PathName.Buffer +=1; + PathName.Length -=1; + } + + for (FileTableEntry->FileNameLength = 0; + (((USHORT)FileTableEntry->FileNameLength < PathName.Length) && + (PathName.Buffer[FileTableEntry->FileNameLength] != '\\')); + FileTableEntry->FileNameLength += 1) { + + FileTableEntry->FileName[FileTableEntry->FileNameLength] = + PathName.Buffer[FileTableEntry->FileNameLength]; + } + + FatFirstComponent( &PathName, &Name ); + + Status = FatSearchForDirent( FatStructureContext, + DeviceId, + CurrentDirectoryIndex, + &Name, + &FileTableEntry->u.FatFileContext.Dirent, + &FileTableEntry->u.FatFileContext.DirentLbo ); + + if (Status == ENOENT) { + + SearchSucceeded = FALSE; + break; + } + + if (Status != ESUCCESS) { + + return Status; + } + + // + // We have a match now check to see if it is a directory, and also + // if it is readonly + // + + IsDirectory = FlagOn( FileTableEntry->u.FatFileContext.Dirent.Attributes, FAT_DIRENT_ATTR_DIRECTORY ); + + IsReadOnly = FlagOn( FileTableEntry->u.FatFileContext.Dirent.Attributes, FAT_DIRENT_ATTR_READ_ONLY ); + + if (IsDirectory) { + + CurrentDirectoryIndex = FileTableEntry->u.FatFileContext.Dirent.FirstClusterOfFile; + } + } + } + + // + // If the path name length is not zero then we were trying to crack a path + // with an nonexistent (or non directory) name in it. For example, we tried + // to crack a\b\c\d and b is not a directory or does not exist (then the path + // name will still contain c\d). + // + + if (PathName.Length != 0) { + + return ENOTDIR; + } + + // + // At this point we've cracked the name up to (an maybe including the last + // component). We located the last component if the SearchSucceeded flag is + // true, otherwise the last component does not exist. If we located the last + // component then this is like an open or a supersede, but not a create. + // + + if (SearchSucceeded) { + + // + // Check if the last component is a directory + // + + if (IsDirectory) { + + // + // For an existing directory the only valid open mode is OpenDirectory + // all other modes return an error + // + + switch (OpenMode) { + + case ArcOpenReadOnly: + case ArcOpenWriteOnly: + case ArcOpenReadWrite: + case ArcCreateWriteOnly: + case ArcCreateReadWrite: + case ArcSupersedeWriteOnly: + case ArcSupersedeReadWrite: + + // + // If we reach here then the caller got a directory but didn't + // want to open a directory + // + + return EISDIR; + + case ArcOpenDirectory: + + // + // If we reach here then the caller got a directory and wanted + // to open a directory. + // + + FileTableEntry->Flags.Open = 1; + FileTableEntry->Flags.Read = 1; + + return ESUCCESS; + + case ArcCreateDirectory: + + // + // If we reach here then the caller got a directory and wanted + // to create a new directory + // + + return EACCES; + } + } + + // + // If we get there then we have an existing file that is being opened. + // We can open existing files through a lot of different open modes in + // some cases we need to check the read only part of file and/or truncate + // the file. + // + + switch (OpenMode) { + + case ArcOpenReadOnly: + + // + // If we reach here then the user got a file and wanted to open the + // file read only + // + + FileTableEntry->Flags.Open = 1; + FileTableEntry->Flags.Read = 1; + + return ESUCCESS; + + case ArcOpenWriteOnly: + case ArcOpenReadWrite: + case ArcCreateWriteOnly: + case ArcCreateReadWrite: + case ArcSupersedeWriteOnly: + case ArcSupersedeReadWrite: + + // + // If we reach here then the user got a file and wanted to create a new + // file or open a file for write. + // + + return EACCES; + + case ArcOpenDirectory: + case ArcCreateDirectory: + + // + // If we reach here then the user got a file and wanted a directory + // + + return ENOTDIR; + } + } + + // + // If we get here the last component does not exist so we are trying to create + // either a new file or a directory. + // + + return EACCES; +} + + +ARC_STATUS +FatRead ( + IN ULONG FileId, + OUT PVOID Buffer, + IN ULONG Length, + OUT PULONG Transfer + ) + +/*++ + +Routine Description: + + This routine reads data from the specified file. + +Arguments: + + FileId - Supplies the file table index. + + Buffer - Supplies a pointer to the buffer that receives the data + read. + + Length - Supplies the number of bytes that are to be read. + + Transfer - Supplies a pointer to a variable that receives the number + of bytes actually transfered. + +Return Value: + + ESUCCESS is returned if the read operation is successful. Otherwise, + an unsuccessful status is returned that describes the reason for failure. + +--*/ + +{ + PBL_FILE_TABLE FileTableEntry; + PFAT_STRUCTURE_CONTEXT FatStructureContext; + ULONG DeviceId; + + FatDebugOutput("FatRead\r\n", 0, 0); + + // + // Load out local variables + // + + FileTableEntry = &BlFileTable[FileId]; + FatStructureContext = (PFAT_STRUCTURE_CONTEXT)FileTableEntry->StructureContext; + DeviceId = FileTableEntry->DeviceId; + + // + // Clear the transfer count + // + + *Transfer = 0; + + // + // Read in runs (i.e., bytes) until the byte count goes to zero + // + + while (Length > 0) { + + LBO Lbo; + + ULONG CurrentRunByteCount; + + // + // Lookup the corresponding Lbo and run length for the current position + // (i.e., Vbo). + // + + if (FatVboToLbo( FileId, FileTableEntry->Position.LowPart, &Lbo, &CurrentRunByteCount ) != ESUCCESS) { + + return ESUCCESS; + } + + // + // while there are bytes to be read in from the current run + // length and we haven't exhausted the request we loop reading + // in bytes. The biggest request we'll handle is only 32KB + // contiguous bytes per physical read. So we might need to loop + // through the run. + // + + while ((Length > 0) && (CurrentRunByteCount > 0)) { + + LONG SingleReadSize; + + // + // Compute the size of the next physical read + // + + SingleReadSize = Minimum(Length, 32 * 1024); + SingleReadSize = Minimum((ULONG)SingleReadSize, CurrentRunByteCount); + + // + // Don't read beyond the eof + // + + if (((ULONG)SingleReadSize + FileTableEntry->Position.LowPart) > + FileTableEntry->u.FatFileContext.Dirent.FileSize) { + + SingleReadSize = FileTableEntry->u.FatFileContext.Dirent.FileSize - + FileTableEntry->Position.LowPart; + + // + // If the readjusted read length is now zero then we're done. + // + + if (SingleReadSize <= 0) { + + return ESUCCESS; + } + + // + // By also setting length here we'll make sure that this is our last + // read + // + + Length = SingleReadSize; + } + + // + // Issue the read + // + + DiskRead( DeviceId, Lbo, SingleReadSize, Buffer); + + // + // Update the remaining length, Current run byte count + // and new Lbo offset + // + + Length -= SingleReadSize; + CurrentRunByteCount -= SingleReadSize; + Lbo += SingleReadSize; + + // + // Update the current position and the number of bytes transfered + // + + FileTableEntry->Position.LowPart += SingleReadSize; + *Transfer += SingleReadSize; + + // + // Update buffer to point to the next byte location to fill in + // + + Buffer = (PCHAR)Buffer + SingleReadSize; + } + } + + // + // If we get here then remaining sector count is zero so we can + // return success to our caller + // + + return ESUCCESS; +} + + +ARC_STATUS +FatSeek ( + IN ULONG FileId, + IN PLARGE_INTEGER Offset, + IN SEEK_MODE SeekMode + ) + +/*++ + +Routine Description: + + This routine seeks to the specified position for the file specified + by the file id. + +Arguments: + + FileId - Supplies the file table index. + + Offset - Supplies the offset in the file to position to. + + SeekMode - Supplies the mode of the seek operation. + +Return Value: + + ESUCCESS is returned if the seek operation is successful. Otherwise, + EINVAL is returned. + +--*/ + +{ + PBL_FILE_TABLE FileTableEntry; + ULONG NewPosition; + + FatDebugOutput("FatSeek\r\n", 0, 0); + + // + // Load our local variables + // + + FileTableEntry = &BlFileTable[FileId]; + + // + // Compute the new position + // + + if (SeekMode == SeekAbsolute) { + + NewPosition = Offset->LowPart; + + } else { + + NewPosition = FileTableEntry->Position.LowPart + Offset->LowPart; + } + + // + // If the new position is greater than the file size then return + // an error + // + + if (NewPosition > FileTableEntry->u.FatFileContext.Dirent.FileSize) { + + return EINVAL; + } + + // + // Otherwise set the new position and return to our caller + // + + FileTableEntry->Position.LowPart = NewPosition; + return ESUCCESS; +} + + +// +// Internal support routine +// + +ARC_STATUS +FatDiskRead ( + IN ULONG DeviceId, + IN LBO Lbo, + IN ULONG ByteCount, + IN PVOID Buffer + ) + +/*++ + +Routine Description: + + This routine reads in zero or more bytes from the specified device. + +Arguments: + + DeviceId - Supplies the device id to use in the arc calls. + + Lbo - Supplies the LBO to start reading from. + + ByteCount - Supplies the number of bytes to read. + + Buffer - Supplies a pointer to the buffer to read the bytes into. + +Return Value: + + ESUCCESS is returned if the read operation is successful. Otherwise, + an unsuccessful status is returned that describes the reason for failure. + +--*/ + +{ + LARGE_INTEGER LargeLbo; + ARC_STATUS Status; + ULONG i; + + // + // Special case the zero byte read request + // + + if (ByteCount == 0) { + + return ESUCCESS; + } + + // + // Seek to the appropriate offset on the volume + // + + LargeLbo.LowPart = (ULONG)Lbo; + LargeLbo.HighPart = 0; + + if ((Status = ArcSeek( DeviceId, &LargeLbo, SeekAbsolute )) != ESUCCESS) { + + return Status; + } + + // + // Issue the arc read request + // + + if ((Status = ArcRead( DeviceId, Buffer, ByteCount, &i)) != ESUCCESS) { + + return Status; + } + + // + // Make sure we got back the amount requested + // + + if (ByteCount != i) { + + return EIO; + } + + // + // Everything is fine so return success to our caller + // + + return ESUCCESS; +} + + +// +// Internal support routine +// + +CLUSTER_TYPE +FatInterpretClusterType ( + IN PFAT_STRUCTURE_CONTEXT FatStructureContext, + IN FAT_ENTRY Entry + ) + +/*++ + +Routine Description: + + This procedure tells the caller how to interpret a fat table entry. It will + indicate if the fat cluster is available, reserved, bad, the last one, or another + fat index. + +Arguments: + + FatStructureContext - Supplies the volume structure for the operation + + DeviceId - Supplies the DeviceId for the volume being used. + + Entry - Supplies the fat entry to examine. + +Return Value: + + The type of the input fat entry is returned + +--*/ + +{ + // + // Check for 12 or 16 bit fat. + // + + if (FatIndexBitSize(&FatStructureContext->Bpb) == 12) { + + // + // For 12 bit fat check for one of the cluster types, but first + // make sure we only looking at 12 bits of the entry + // + + Entry &= 0x00000fff; + + if (Entry == 0x000) { return FatClusterAvailable; } + else if ((Entry >= 0xff0) && (Entry <= 0xff6)) { return FatClusterReserved; } + else if (Entry == 0xff7) { return FatClusterBad; } + else if ((Entry >= 0xff8) && (Entry <= 0xfff)) { return FatClusterLast; } + else { return FatClusterNext; } + + } else { + + // + // For 16 bit fat check for one of the cluster types, but first + // make sure we are only looking at 16 bits of the entry + // + + Entry &= 0x0000ffff; + + if (Entry == 0x0000) { return FatClusterAvailable; } + else if ((Entry >= 0xfff0) && (Entry <= 0xfff6)) { return FatClusterReserved; } + else if (Entry == 0xfff7) { return FatClusterBad; } + else if ((Entry >= 0xfff8) && (Entry <= 0xffff)) { return FatClusterLast; } + else { return FatClusterNext; } + } +} + + +// +// Internal support routine +// + +ARC_STATUS +FatLookupFatEntry ( + IN PFAT_STRUCTURE_CONTEXT FatStructureContext, + IN ULONG DeviceId, + IN FAT_ENTRY FatIndex, + OUT PFAT_ENTRY FatEntry + ) + +/*++ + +Routine Description: + + This routine returns the value stored within the fat table and the specified + fat index. It is semantically equivalent to doing + + x = Fat[FatIndex] + +Arguments: + + FatStrutureContext - Supplies the volume struture being used + + DeviceId - Supplies the device being used + + FatIndex - Supplies the index being looked up. + + FatEntry - Receives the value stored at the specified fat index + +Return Value: + + ESUCCESS is returned if the operation is successful. Otherwise, + an unsuccessful status is returned that describes the reason for failure. + +--*/ + +{ + BOOLEAN TwelveBitFat; + VBO Vbo; + + // + // Calculate the Vbo of the word in the fat we need and + // also figure out if this is a 12 or 16 bit fat + // + + if (FatIndexBitSize( &FatStructureContext->Bpb ) == 12) { + + TwelveBitFat = TRUE; + Vbo = (FatIndex * 3) / 2; + + } else { + + TwelveBitFat = FALSE; + Vbo = FatIndex * 2; + } + + // + // Check if the Vbo we need is already in the cached fat + // + + if ((FatStructureContext->CachedFat == NULL) || + (Vbo < FatStructureContext->CachedFatVbo) || + ((Vbo+1) > (FatStructureContext->CachedFatVbo + FAT_CACHE_SIZE))) { + + // + // Set the aligned cached fat buffer in the structure context + // + + FatStructureContext->CachedFat = ALIGN_BUFFER( &FatStructureContext->CachedFatBuffer[0] ); + + // + // Now set the new cached Vbo to be the Vbo of the cache sized section that + // we're trying to map. Each time we read in the cache we only read in + // cache sized and cached aligned pieces of the fat. So first compute an + // aligned cached fat vbo and then do the read. + // + + FatStructureContext->CachedFatVbo = (Vbo / FAT_CACHE_SIZE) * FAT_CACHE_SIZE; + + DiskRead( DeviceId, + FatStructureContext->CachedFatVbo + FatFirstFatAreaLbo(&FatStructureContext->Bpb), + FAT_CACHE_SIZE, + FatStructureContext->CachedFat ); + } + + // + // At this point the cached fat contains the vbo we're after so simply + // extract the word + // + + CopyUchar2( FatEntry, + &FatStructureContext->CachedFat[Vbo - FatStructureContext->CachedFatVbo] ); + + // + // Now if this is a 12 bit fat then check if the index is odd or even + // If it is odd then we need to shift it over 4 bits, and in all + // cases we need to mask out the high 4 bits. + // + + if (TwelveBitFat) { + + if ((FatIndex % 2) == 1) { *FatEntry >>= 4; } + + *FatEntry &= 0x0fff; + } + + return ESUCCESS; +} + + +// +// Internal support routine +// + +LBO +FatIndexToLbo ( + IN PFAT_STRUCTURE_CONTEXT FatStructureContext, + IN FAT_ENTRY FatIndex + ) + +/*++ + +Routine Description: + + This procedure translates a fat index into its corresponding lbo. + +Arguments: + + FatStructureContext - Supplies the volume structure for the operation + + Entry - Supplies the fat entry to examine. + +Return Value: + + The LBO for the input fat index is returned + +--*/ + +{ + // + // The formula for translating an index into an lbo is to take the index subtract + // 2 (because index values 0 and 1 are reserved) multiply that by the bytes per + // cluster and add the results to the first file area lbo. + // + + return ((FatIndex-2) * FatBytesPerCluster(&FatStructureContext->Bpb)) + + FatFileAreaLbo(&FatStructureContext->Bpb); +} + + +// +// Internal support routine +// + +VOID +FatLboToIndex ( + IN PFAT_STRUCTURE_CONTEXT FatStructureContext, + IN LBO Lbo, + OUT PFAT_ENTRY FatIndex, + OUT PULONG ByteOffset + ) + +/*++ + +Routine Description: + + This procedure translates an lbo into its corresponding fat index and byte offset. + +Arguments: + + FatStructureContext - Supplies the volume structure for the operation + + Lbo - Supplies the lbo to translate from. + + FatIndex - Receives the fat index of the cluster containing the input lbo. + + ByteOffset - Receives the bytes offset of the lbo within the specified cluster. + +Return Value: + + None. + +--*/ + +{ + // + // The formula to translate an lbo into and index is to subtract out the + // file area lbo offset, divide by the number of bytes per cluster and then add 2. + // + + *FatIndex = (FAT_ENTRY)(((Lbo - FatFileAreaLbo(&FatStructureContext->Bpb)) + / FatBytesPerCluster(&FatStructureContext->Bpb)) + 2); + + // + // The byte offset if simply the lbo modulo the number of bytes per cluster + // + + *ByteOffset = Lbo % FatBytesPerCluster(&FatStructureContext->Bpb); + + return; +} + + +// +// Internal support routine +// + +ARC_STATUS +FatSearchForDirent ( + IN PFAT_STRUCTURE_CONTEXT FatStructureContext, + IN ULONG DeviceId, + IN FAT_ENTRY DirectoriesStartingIndex, + IN PFAT8DOT3 FileName, + OUT PDIRENT Dirent, + OUT PLBO Lbo + ) + +/*++ + +Routine Description: + + The procedure searches the indicated directory for a dirent that matches + the input file name. + +Arguments: + + FatStructureContext - Supplies the structure context for the operation + + DeviceId - Supplies the Device id for the operation + + DirectoriesStartingIndex - Supplies the fat index of the directory we are + to search. A value of zero indicates that we are searching the root directory + + FileName - Supplies the file name to look for. The name must have already been + biased by the 0xe5 transmogrification + + Dirent - The caller supplies the memory for a dirent and this procedure will + fill in the dirent if one is located + + Lbo - Receives the Lbo of the dirent if one is located + +Return Value: + + ESUCCESS is returned if the operation is successful. Otherwise, + an unsuccessful status is returned that describes the reason for failure. + +--*/ + +{ + PDIRENT DirentBuffer; + UCHAR Buffer[ 16 * sizeof(DIRENT) + 256 ]; + + ULONG i; + ULONG j; + + ULONG BytesPerCluster; + FAT_ENTRY FatEntry; + CLUSTER_TYPE ClusterType; + + DirentBuffer = (PDIRENT)ALIGN_BUFFER( &Buffer[0] ); + + // + // Check if this is the root directory that is being searched + // + + if (DirectoriesStartingIndex == FAT_CLUSTER_AVAILABLE) { + + VBO Vbo; + + ULONG RootLbo = FatRootDirectoryLbo(&FatStructureContext->Bpb); + ULONG RootSize = FatRootDirectorySize(&FatStructureContext->Bpb); + + // + // For the root directory we'll zoom down the dirents until we find + // a match, or run out of dirents or hit the never used dirent. + // The outer loop reads in 512 bytes of the directory at a time into + // dirent buffer. + // + + for (Vbo = 0; Vbo < RootSize; Vbo += 16 * sizeof(DIRENT)) { + + *Lbo = Vbo + RootLbo; + + DiskRead( DeviceId, *Lbo, 16 * sizeof(DIRENT), DirentBuffer ); + + // + // The inner loop cycles through the 16 dirents that we've just read in + // + + for (i = 0; i < 16; i += 1) { + + // + // Check if we've found a non label match for file name, and if so + // then copy the buffer into the dirent and set the real lbo + // of the dirent and return + // + + if (!FlagOn(DirentBuffer[i].Attributes, FAT_DIRENT_ATTR_VOLUME_ID ) && + AreNamesEqual(&DirentBuffer[i].FileName, FileName)) { + + for (j = 0; j < sizeof(DIRENT); j += 1) { + + ((PCHAR)Dirent)[j] = ((PCHAR)DirentBuffer)[(i * sizeof(DIRENT)) + j]; + } + + *Lbo = Vbo + RootLbo + (i * sizeof(DIRENT)); + + return ESUCCESS; + } + + if (DirentBuffer[i].FileName[0] == FAT_DIRENT_NEVER_USED) { + + return ENOENT; + } + } + } + + return ENOENT; + } + + // + // If we get here we need to search a non-root directory. The alrogithm + // for doing the search is that for each cluster we read in each dirent + // until we find a match, or run out of clusters, or hit the never used + // dirent. First set some local variables and then get the cluster type + // of the first cluster + // + + BytesPerCluster = FatBytesPerCluster( &FatStructureContext->Bpb ); + FatEntry = DirectoriesStartingIndex; + ClusterType = FatInterpretClusterType( FatStructureContext, FatEntry ); + + // + // Now loop through each cluster, and compute the starting Lbo for each cluster + // that we encounter + // + + while (ClusterType == FatClusterNext) { + + LBO ClusterLbo; + ULONG Offset; + + ClusterLbo = FatIndexToLbo( FatStructureContext, FatEntry ); + + // + // Now for each dirent in the cluster compute the lbo, read in the dirent + // and check for a match, the outer loop reads in 512 bytes of dirents at + // a time. + // + + for (Offset = 0; Offset < BytesPerCluster; Offset += 16 * sizeof(DIRENT)) { + + *Lbo = Offset + ClusterLbo; + + DiskRead( DeviceId, *Lbo, 16 * sizeof(DIRENT), DirentBuffer ); + + // + // The inner loop cycles through the 16 dirents that we've just read in + // + + for (i = 0; i < 16; i += 1) { + + // + // Check if we've found a for file name, and if so + // then copy the buffer into the dirent and set the real lbo + // of the dirent and return + // + + if (AreNamesEqual(&DirentBuffer[i].FileName, FileName)) { + + for (j = 0; j < sizeof(DIRENT); j += 1) { + + ((PCHAR)Dirent)[j] = ((PCHAR)DirentBuffer)[(i * sizeof(DIRENT)) + j]; + } + + *Lbo = Offset + ClusterLbo + (i * sizeof(DIRENT)); + + return ESUCCESS; + } + + if (DirentBuffer[i].FileName[0] == FAT_DIRENT_NEVER_USED) { + + return ENOENT; + } + } + } + + // + // Now that we've exhausted the current cluster we need to read + // in the next cluster. So locate the next fat entry in the chain + // and go back to the top of the while loop. + // + + LookupFatEntry( FatStructureContext, DeviceId, FatEntry, &FatEntry ); + + ClusterType = FatInterpretClusterType(FatStructureContext, FatEntry); + } + + return ENOENT; +} + + +// +// Internal support routine +// + +ARC_STATUS +FatLoadMcb ( + IN ULONG FileId, + IN VBO StartingVbo + ) + +/*++ + +Routine Description: + + This routine loads into the cached mcb table the the retrival information for + the starting vbo. + +Arguments: + + FileId - Supplies the FileId for the operation + + StartingVbo - Supplies the starting vbo to use when loading the mcb + +Return Value: + + ESUCCESS is returned if the operation is successful. Otherwise, + an unsuccessful status is returned that describes the reason for failure. + +--*/ + +{ + PBL_FILE_TABLE FileTableEntry; + PFAT_STRUCTURE_CONTEXT FatStructureContext; + PFAT_MCB Mcb; + ULONG DeviceId; + ULONG BytesPerCluster; + + FAT_ENTRY FatEntry; + CLUSTER_TYPE ClusterType; + VBO Vbo; + + // + // Preload some of the local variables + // + + FileTableEntry = &BlFileTable[FileId]; + FatStructureContext = (PFAT_STRUCTURE_CONTEXT)FileTableEntry->StructureContext; + Mcb = &FatStructureContext->Mcb; + DeviceId = FileTableEntry->DeviceId; + BytesPerCluster = FatBytesPerCluster(&FatStructureContext->Bpb); + + // + // Set the file id in the structure context, and also set the mcb to be initially + // empty + // + + FatStructureContext->FileId = FileId; + Mcb->InUse = 0; + Mcb->Vbo[0] = 0; + + // + // Check if this is the root directory. If it is then we build the single + // run mcb entry for the root directory. + // + + if (FileTableEntry->u.FatFileContext.DirentLbo == 0) { + + Mcb->InUse = 1; + Mcb->Lbo[0] = FatRootDirectoryLbo(&FatStructureContext->Bpb); + Mcb->Vbo[1] = FatRootDirectorySize(&FatStructureContext->Bpb); + + return ESUCCESS; + } + + // + // For all other files/directories we need to do some work. First get the fat + // entry and cluster type of the fat entry stored in the dirent + // + + FatEntry = FileTableEntry->u.FatFileContext.Dirent.FirstClusterOfFile; + ClusterType = FatInterpretClusterType(FatStructureContext, FatEntry); + + // + // Scan through the fat until we reach the vbo we're after and then build the + // mcb for the file + // + + for (Vbo = BytesPerCluster; Vbo < StartingVbo; Vbo += BytesPerCluster) { + + // + // Check if the file does not have any allocation beyond this point in which + // case the mcb we return is empty + // + + if (ClusterType != FatClusterNext) { + + return ESUCCESS; + } + + LookupFatEntry( FatStructureContext, DeviceId, FatEntry, &FatEntry ); + + ClusterType = FatInterpretClusterType(FatStructureContext, FatEntry); + } + + // + // We need to check again if the file does not have any allocation beyond this + // point in which case the mcb we return is empty + // + + if (ClusterType != FatClusterNext) { + + return ESUCCESS; + } + + // + // At this point FatEntry denotes another cluster, and it happens to be the + // cluster we want to start loading into the mcb. So set up the first run in + // the mcb to be this cluster, with a size of a single cluster. + // + + Mcb->InUse = 1; + Mcb->Vbo[0] = Vbo - BytesPerCluster; + Mcb->Lbo[0] = FatIndexToLbo( FatStructureContext, FatEntry ); + Mcb->Vbo[1] = Vbo; + + // + // Now we'll scan through the fat chain until we either exhaust the fat chain + // or we fill up the mcb + // + + while (TRUE) { + + LBO Lbo; + + // + // Get the next fat entry and interpret its cluster type + // + + LookupFatEntry( FatStructureContext, DeviceId, FatEntry, &FatEntry ); + + ClusterType = FatInterpretClusterType(FatStructureContext, FatEntry); + + if (ClusterType != FatClusterNext) { + + return ESUCCESS; + } + + // + // Now calculate the lbo for this cluster and determine if it + // is a continuation of the previous run or a start of a new run + // + + Lbo = FatIndexToLbo(FatStructureContext, FatEntry); + + // + // It is a continuation if the lbo of the last run plus the current + // size of the run is equal to the lbo for the next cluster. If it + // is a contination then we only need to add a cluster amount to the + // last vbo to increase the run size. If it is a new run then + // we need to check if the run will fit, and if so then add in the + // new run. + // + + if ((Mcb->Lbo[Mcb->InUse-1] + (Mcb->Vbo[Mcb->InUse] - Mcb->Vbo[Mcb->InUse-1])) == Lbo) { + + Mcb->Vbo[Mcb->InUse] += BytesPerCluster; + + } else { + + if ((Mcb->InUse + 1) >= FAT_MAXIMUM_MCB) { + + return ESUCCESS; + } + + Mcb->InUse += 1; + Mcb->Lbo[Mcb->InUse-1] = Lbo; + Mcb->Vbo[Mcb->InUse] = Mcb->Vbo[Mcb->InUse-1] + BytesPerCluster; + } + } + + return ESUCCESS; +} + + +// +// Internal support routine +// + +ARC_STATUS +FatVboToLbo ( + IN ULONG FileId, + IN VBO Vbo, + OUT PLBO Lbo, + OUT PULONG ByteCount + ) + +/*++ + +Routine Description: + + This routine computes the run denoted by the input vbo to into its + corresponding lbo and also returns the number of bytes remaining in + the run. + +Arguments: + + Vbo - Supplies the Vbo to match + + Lbo - Recieves the corresponding Lbo + + ByteCount - Receives the number of bytes remaining in the run + +Return Value: + + ESUCCESS is returned if the operation is successful. Otherwise, + an unsuccessful status is returned that describes the reason for failure. + +--*/ + +{ + PFAT_STRUCTURE_CONTEXT FatStructureContext; + PFAT_MCB Mcb; + ULONG i; + + FatStructureContext = (PFAT_STRUCTURE_CONTEXT)BlFileTable[FileId].StructureContext; + Mcb = &FatStructureContext->Mcb; + + // + // Check if the mcb is for the correct file id and has the range we're asking for. + // If it doesn't then call load mcb to load in the right range. + // + + if ((FileId != FatStructureContext->FileId) || + (Vbo < Mcb->Vbo[0]) || (Vbo >= Mcb->Vbo[Mcb->InUse])) { + + LoadMcb(FileId, Vbo); + } + + // + // Now search for the slot where the Vbo fits in the mcb. Note that + // we could also do a binary search here but because the run count + // is probably small the extra overhead of a binary search doesn't + // buy us anything + // + + for (i = 0; i < Mcb->InUse; i += 1) { + + // + // We found our slot if the vbo we're after is less then the + // next mcb's vbo + // + + if (Vbo < Mcb->Vbo[i+1]) { + + // + // Compute the corresponding lbo which is the stored lbo plus + // the difference between the stored vbo and the vbo we're + // looking up. Also compute the byte count which is the + // difference between the current vbo we're looking up and + // the vbo for the next run. + // + + *Lbo = Mcb->Lbo[i] + (Vbo - Mcb->Vbo[i]); + + *ByteCount = Mcb->Vbo[i+1] - Vbo; + + // + // and return success to our caller + // + + return ESUCCESS; + } + } + + // + // If we really reach here we have an error, most likely because the file is + // not large enough for the requested Vbo. + // + + return EINVAL; +} + + +// +// Internal support routine +// + +VOID +FatFirstComponent ( + IN OUT PSTRING String, + OUT PFAT8DOT3 FirstComponent + ) + +/*++ + +Routine Description: + + Convert a string into fat 8.3 format and advance the input string + descriptor to point to the next file name component. + +Arguments: + + InputString - Supplies a pointer to the input string descriptor. + + Output8dot3 - Supplies a pointer to the converted string. + +Return Value: + + None. + +--*/ + +{ + ULONG Extension; + ULONG Index; + + // + // Fill the output name with blanks. + // + + for (Index = 0; Index < 11; Index += 1) { (*FirstComponent)[Index] = ' '; } + + // + // Copy the first part of the file name up to eight characters and + // skip to the end of the name or the input string as appropriate. + // + + for (Index = 0; Index < String->Length; Index += 1) { + + if ((String->Buffer[Index] == '\\') || (String->Buffer[Index] == '.')) { + + break; + } + + if (Index < 8) { + + (*FirstComponent)[Index] = (CHAR)ToUpper(String->Buffer[Index]); + } + } + + // + // Check if the end of the string was reached, an extension was specified, + // or a subdirectory was specified.. + // + + if (Index < String->Length) { + + if (String->Buffer[Index] == '.') { + + // + // Skip over the extension separator and add the extension to + // the file name. + // + + Index += 1; + Extension = 8; + + while (Index < String->Length) { + + if (String->Buffer[Index] == '\\') { + + break; + } + + if (Extension < 11) { + + (*FirstComponent)[Extension] = (CHAR)ToUpper(String->Buffer[Index]); + Extension += 1; + } + + Index += 1; + } + } + } + + // + // Now we'll bias the first component by the 0xe5 factor so that all our tests + // to names on the disk will be ready for a straight 11 byte comparison + // + + if ((*FirstComponent)[0] == 0xe5) { + + (*FirstComponent)[0] = FAT_DIRENT_REALLY_0E5; + } + + // + // Update string descriptor. + // + + String->Buffer += Index; + String->Length -= Index; + + return; +} + + +ARC_STATUS +FatGetDirectoryEntry ( + IN ULONG FileId, + IN DIRECTORY_ENTRY *DirEntry, + IN ULONG NumberDir, + OUT PULONG CountDir + ) +/*++ + +Routine Description: + + This routine implements the GetDirectoryEntry operation for the + FAT file system. + +Arguments: + + FileId - Supplies the file table index. + + DirEntry - Supplies a pointer to a directory entry structure. + + NumberDir - Supplies the number of directory entries to read. + + Count - Supplies a pointer to a variable to receive the number + of entries read. + +Return Value: + + ESUCCESS is returned if the read was successful, otherwise + an error code is returned. + +--*/ + +{ + // + // define local variables + // + + ARC_STATUS Status; // ARC status + ULONG Count = 0; // # of bytes read + ULONG Position; // file position + PFAT_FILE_CONTEXT pContext; // FAT file context + ULONG RunByteCount = 0; // max sequential bytes + ULONG RunDirCount; // max dir entries to read per time + ULONG i; // general index + PDIRENT FatDirEnt; // directory entry pointer + UCHAR Buffer[ 16 * sizeof(DIRENT) + 32 ]; + LBO Lbo; + BOOLEAN EofDir = FALSE; // not end of file + + // + // initialize local variables + // + + pContext = &BlFileTable[ FileId ].u.FatFileContext; + FatDirEnt = (PDIRENT)ALIGN_BUFFER( &Buffer[0] ); + + // + // if not directory entry, exit with error + // + + if ( !(pContext->Dirent.Attributes & FAT_DIRENT_ATTR_DIRECTORY) ) { + return EBADF; + } + + // + // Initialize the output count to zero + // + + *CountDir = 0; + + // + // if NumberDir is zero, return ESUCCESS. + // + + if ( !NumberDir ) { + return ESUCCESS; + } + + // + // read one directory at a time. + // + + do + { + // + // save position + // + + Position = BlFileTable[ FileId ].Position.LowPart; + + // + // Lookup the corresponding Lbo and run length for the current position + // + + if ( !RunByteCount ) { + if (Status = FatVboToLbo( FileId, Position, &Lbo, &RunByteCount )) { + if ( Status == EINVAL ) { + break; // eof has been reached + } else { + return Status; // I/O error + } + } + } + + // + // validate the # of bytes readable in sequance (exit loop if eof) + // the block is always multiple of a directory entry size. + // + + if ( !(RunDirCount = Minimum( RunByteCount/sizeof(DIRENT), 16)) ) { + break; + } + + // + // issue the read + // + + if ( Status = FatDiskRead( BlFileTable[ FileId ].DeviceId, + Lbo, + RunDirCount * sizeof(DIRENT), + (PVOID)FatDirEnt )) { + BlFileTable[ FileId ].Position.LowPart = Position; + return Status; + } + + for ( i=0; i<RunDirCount; i++ ) { + // + // exit from loop if logical end of directory + // + + if ( FatDirEnt[i].FileName[0] == FAT_DIRENT_NEVER_USED ) { + EofDir = TRUE; + break; + } + + // + // update the current position and the number of bytes transfered + // + + BlFileTable[ FileId ].Position.LowPart += sizeof(DIRENT); + Lbo += sizeof(DIRENT); + RunByteCount -= sizeof(DIRENT); + + // + // skip this entry if the file or directory has been erased + // + + if ( FatDirEnt[i].FileName[0] == FAT_DIRENT_DELETED ) { + continue; + } + + // + // skip this entry if this is a valume label + // + + if ( FatDirEnt[i].Attributes & FAT_DIRENT_ATTR_VOLUME_ID ) { + continue; + } + + // + // convert FAT directory entry in ARC directory entry + // + + FatDirToArcDir( &FatDirEnt[i], DirEntry++ ); + + // + // update pointers + // + + if ( ++*CountDir >= NumberDir ) { + break; + } + } + } + while ( !EofDir && *CountDir < NumberDir ); + + // + // all done + // + + return *CountDir ? ESUCCESS : ENOTDIR; +} + + +/*++ + +Routine Description: + + This routine converts a FAT directory entry into an ARC + directory entry. + +Arguments: + + FatDirEntry - supplies a pointer to a FAT directory entry. + + ArcDirEntry - supplies a pointer to an ARC directory entry. + +Return Value: + + None. + +--*/ + +VOID +FatDirToArcDir ( + IN PDIRENT FatDirEnt, + OUT PDIRECTORY_ENTRY ArcDirEnt + ) +{ + ULONG i, e; + + // + // clear info area + // + + RtlZeroMemory( ArcDirEnt, sizeof(DIRECTORY_ENTRY) ); + + // + // check the directory flag + // + + if ( FatDirEnt->Attributes & FAT_DIRENT_ATTR_DIRECTORY ) { + ArcDirEnt->FileAttribute |= ArcDirectoryFile; + } + + // + // check the read-only flag + // + + if ( FatDirEnt->Attributes & FAT_DIRENT_ATTR_READ_ONLY ) { + ArcDirEnt->FileAttribute |= ArcReadOnlyFile; + } + + // + // clear name string + // + + RtlZeroMemory( ArcDirEnt->FileName, 32 ); + + // + // copy first portion of file name + // + + for ( i = 0; i < 8 && FatDirEnt->FileName[i] != ' '; i++ ) { + ArcDirEnt->FileName[i] = FatDirEnt->FileName[i]; + } + + // + // check for an extension + // + + if ( FatDirEnt->FileName[8] != ' ' ) { + + // + // store the dot char + // + + ArcDirEnt->FileName[i++] = '.'; + + // + // add the extension + // + + for ( e=8; e<11 && FatDirEnt->FileName[e] != ' '; e++ ) { + ArcDirEnt->FileName[i++] = FatDirEnt->FileName[e]; + } + } + + // + // set file name length before returning + // + + ArcDirEnt->FileNameLength = i; + + return; +} diff --git a/private/ntos/fw/duobase/mips/fatboot.h b/private/ntos/fw/duobase/mips/fatboot.h new file mode 100644 index 000000000..ccd548160 --- /dev/null +++ b/private/ntos/fw/duobase/mips/fatboot.h @@ -0,0 +1,270 @@ +/*++ + +Copyright (c) 1991 Microsoft Corporation + +Module Name: + + fatboot.h + +Abstract: + + This module defines globally used procedure and data structures used + by fat boot. + +Author: + + Gary Kimura (garyki) 29-Aug-1989 + +Revision History: + +--*/ + +#ifndef _FATBOOT_ +#define _FATBOOT_ +#include "fat.h" + + +// +// The following structure is used to define the local mcb structure used within +// the fat boot loader to maintain a small cache of the retrieval information +// for a single file/directory +// + +#define FAT_MAXIMUM_MCB (41) + +typedef struct _FAT_MCB { + + // + // The following fields indicate the number of entries in use by + // the boot mcb. and the boot mcb itself. The boot mcb is + // just a collection of [vbo, lbo] pairs. The last InUse entry + // Lbo's value is ignored, because it is only used to give the + // length of the previous run. + // + + ULONG InUse; + + VBO Vbo[ FAT_MAXIMUM_MCB ]; + LBO Lbo[ FAT_MAXIMUM_MCB ]; + +} FAT_MCB, *PFAT_MCB; + + + +#ifdef SETUP + + // + // The following structure describes a cached FAT volume. When a file + // is opened, the device id stored in the file table is matched against + // a list of cached device ids. If a match is found, the file's structure + // context is set to use the volume's cache. + // + +#define MAX_REFS_PER_CACHED_FAT_VOL 5 + +typedef struct _FAT_VOLUME { + ULONG DeviceId[MAX_REFS_PER_CACHED_FAT_VOL]; + ULONG DeviceIdReferences[MAX_REFS_PER_CACHED_FAT_VOL]; + ULONG OpenFileCount; + ULONG References; + PCHAR DeviceName; + PUCHAR FatSectorDirty; + PUCHAR Fat; + PUCHAR RootDirCache; + PUCHAR UnalignedFat; + PUCHAR UnalignedRootDir; +} FAT_VOLUME, *PFAT_VOLUME; + +#endif + + + +// +// The following structure is used to define the geometry of the fat volume +// There is one for every mounted volume. It describes the size/configuration +// of the volume, contains a small cached mcb for the last file being accessed +// on the volume, and contains a small cache of the fat. Given a FileId we +// can access the structure context through the structure context field in the +// global BlFileTable (e.g., BlFileTable[FileId].StructureContext). +// + +// +// The following constant is used to determine how much of the fat we keep +// cached in memory at any one time. It must be a multiple of 6 bytes in order to +// hold complete 12 and 16 bit fat entries in the cache at any one time. +// + +#define FAT_CACHE_SIZE (512*3) + +typedef struct _FAT_STRUCTURE_CONTEXT { + + // + // The following field contains an unpacked copy of the bios parameter block + // for the mounted volume + // + + BIOS_PARAMETER_BLOCK Bpb; + + // + // The following two fields contain current file id of the file/directory + // whose mcb we are keeping around, and the second field is the mcb itself + // + + ULONG FileId; + FAT_MCB Mcb; + + // + // The following fields describe/contain the current cached fat. The vbo + // is the smallest vbo of the fat currently in the cache, and cached fat + // is a pointer to the cached data. The extra buffer/indirectiion is needed + // to keep everything aligned properly. The dirty flag is used to indicate + // if the current cached fat has been modified and needs to be flushed to disk. + // Vbo is used because this allows us to do a lot of our computations having + // already biased lbo offset to the first fat table. + // + + BOOLEAN CachedFatDirty; + VBO CachedFatVbo; + PUCHAR CachedFat; + UCHAR CachedFatBuffer[ FAT_CACHE_SIZE + 256 ]; + +#ifdef SETUP + PFAT_VOLUME FatVolume; +#endif + +} FAT_STRUCTURE_CONTEXT, *PFAT_STRUCTURE_CONTEXT; + +// +// The following structure is used to define the location and size of each +// opened file. There is one of these of every opened file. It is part of +// the union of a BL_FILE_TABLE structuure. Given a FileId we can access the +// file context via the BlFileTable (e.g., BlFileTable[FileId].u.FatFileContext) +// + +typedef struct _FAT_FILE_CONTEXT { + + // + // The following two fields describe where on the disk the dirent for the + // file is located and also contains a copy of the dirent + // + + LBO DirentLbo; + DIRENT Dirent; + +#ifdef SETUP + // + // Fat index of the first cluster of the directory containing the file. + // Needed for rename operations. + // + FAT_ENTRY DirectoryFatIndex; +#endif + +} FAT_FILE_CONTEXT, *PFAT_FILE_CONTEXT; + + +// +// Define file I/O prototypes. +// + +ARC_STATUS +FatClose ( + IN ULONG FileId + ); + +ARC_STATUS +FatMount ( + IN PCHAR MountPath, + IN MOUNT_OPERATION Operation + ); + +ARC_STATUS +FatOpen ( + IN PCHAR OpenPath, + IN OPEN_MODE OpenMode, + OUT PULONG FileId + ); + +ARC_STATUS +FatRead ( + IN ULONG FileId, + OUT PVOID Buffer, + IN ULONG Length, + OUT PULONG Count + ); + +ARC_STATUS +FatGetReadStatus ( + IN ULONG FileId + ); + +ARC_STATUS +FatSeek ( + IN ULONG FileId, + IN PLARGE_INTEGER Offset, + IN SEEK_MODE SeekMode + ); + +ARC_STATUS +FatWrite ( + IN ULONG FileId, + IN PVOID Buffer, + IN ULONG Length, + OUT PULONG Count + ); + +ARC_STATUS +FatGetFileInformation ( + IN ULONG FileId, + OUT PFILE_INFORMATION Buffer + ); + +ARC_STATUS +FatSetFileInformation ( + IN ULONG FileId, + IN ULONG AttributeFlags, + IN ULONG AttributeMask + ); + + +ARC_STATUS +FatRename( + IN ULONG FileId, + IN PCHAR NewFileName + ); + +ARC_STATUS +FatGetDirectoryEntry ( + IN ULONG FileId, + IN DIRECTORY_ENTRY *DirEntry, + IN ULONG NumberDir, + OUT PULONG CountDir + ); + + + +#ifdef SETUP + +// +// The following functions are not part of the standard FAT filesystem for +// the osloader and firmware, but are used in the setupldr environment. +// + + +ARC_STATUS +FatGetFreeSpace( + IN ULONG FileId, + OUT PULONG FreeSpace + ); + +ARC_STATUS +FatCacheVolume ( + IN BOOLEAN StartCaching, + IN PVOID Context, + IN ULONG DeviceId, + IN PCHAR DeviceName + ); + +#endif // def SETUP + + +#endif // _FATBOOT_ diff --git a/private/ntos/fw/duobase/mips/firmware.h b/private/ntos/fw/duobase/mips/firmware.h new file mode 100644 index 000000000..8f8a03e00 --- /dev/null +++ b/private/ntos/fw/duobase/mips/firmware.h @@ -0,0 +1,135 @@ +/*++ + +Copyright (c) 1991 Microsoft Corporation + +Module Name: + + firmware.h + +Abstract: + + This module is the header file that describes the Jazz ARC compliant + firmware. + +Author: + + David N. Cutler (davec) 18-May-1991 + +Revision History: + +--*/ + +#ifndef _FIRMWARE_ +#define _FIRMWARE_ + +// +// Define size of memory allocation table. +// + +#define FW_MEMORY_TABLE_SIZE 10 + +// +// Define COFF image type expected. +// + +#define IMAGE_TYPE_R3000 0x162 +#define IMAGE_TYPE_R4000 0x166 + +// +// Define memory allocation structure. +// + +typedef struct _FW_MEMORY_DESCRIPTOR { + LIST_ENTRY ListEntry; + MEMORY_DESCRIPTOR MemoryEntry; +} FW_MEMORY_DESCRIPTOR, *PFW_MEMORY_DESCRIPTOR; + +// +// Define console output driver prototypes. +// + +NTSTATUS +ConsoleOutBootInitialize ( + ); + +NTSTATUS +ConsoleOutBootOpen ( + IN ULONG FileId + ); + +// +// Define floppy driver prototypes. +// + +NTSTATUS +FloppyBootInitialize ( + ); + +NTSTATUS +FloppyBootOpen ( + IN ULONG FileId + ); + +// +// Define hard disk driver procedure prototypes. +// + +NTSTATUS +HardDiskBootInitialize ( +#if defined(DECSTATION) + IN UCHAR DeviceUnit +#endif + ); + +NTSTATUS +HardDiskBootOpen ( + IN ULONG FileId + ); + +// +// Define firmware routine prototypes. +// + +VOID +FwConfigurationInitialize ( + VOID + ); + +VOID +FwGenerateDescriptor ( + IN PFW_MEMORY_DESCRIPTOR MemoryDescriptor, + IN MEMORY_TYPE MemoryType, + IN ULONG BasePage, + IN ULONG PageCount + ); + +VOID +FwInitialize ( + IN ULONG MemorySize + ); + +VOID +FwIoInitialize ( + VOID + ); + +ARC_STATUS +FwLoadImage( + IN PCHAR LoadFile, + OUT PVOID *TransferRoutine + ); + +VOID +FwMemoryInitialize ( + IN ULONG MemorySize + ); + +// +// Define memory listhead, allocation entries, and free index. +// + +extern ULONG FwMemoryFree; +extern LIST_ENTRY FwMemoryListHead; +extern FW_MEMORY_DESCRIPTOR FwMemoryTable[FW_MEMORY_TABLE_SIZE]; + +#endif // _FIRMWARE_ diff --git a/private/ntos/fw/duobase/mips/fwio.c b/private/ntos/fw/duobase/mips/fwio.c new file mode 100644 index 000000000..a4282a719 --- /dev/null +++ b/private/ntos/fw/duobase/mips/fwio.c @@ -0,0 +1,1277 @@ +/*++ + +Copyright (c) 1991 Microsoft Corporation + +Module Name: + + fwio.c + +Abstract: + + This module implements the ARC firmware I/O operations for a MIPS + R3000 or R3000 Jazz system. + +Author: + + David N. Cutler (davec) 14-May-1991 + + +Revision History: + + Lluis Abello (lluis) 20-Jun-1991 + +--*/ + +#include "fwp.h" +#include "string.h" +#include "duobase.h" + + +// +// Define file table. +// + +BL_FILE_TABLE BlFileTable[BL_FILE_TABLE_SIZE]; + +#define DEVICE_DEVICE 0xDEAD + +// +// Declare the table of opened devices. +// +OPENED_PATHNAME_ENTRY OpenedPathTable[SIZE_OF_OPENED_PATHNAME_TABLE]; + +// +// Declare the table of opened drivers. +// +DRIVER_LOOKUP_ENTRY DeviceLookupTable[SIZE_OF_LOOKUP_TABLE]; +//PCHAR NotEnoughEntriesMsg = "Error: Not enough entries in the lookup table.\n"; + +// +// Define data structure for the file system structure context. +// + +typedef union _FILE_SYSTEM_STRUCTURE { + FAT_STRUCTURE_CONTEXT FatContext; + ULONG Tmp; +} FILE_SYSTEM_STRUCTURE, *PFILE_SYSTEM_STRUCTURE; + +typedef struct _FS_POOL_ENTRY { + BOOLEAN InUse; + FILE_SYSTEM_STRUCTURE Fs; +} FS_POOL_ENTRY, *PFS_POOL_ENTRY; + +#define FS_POOL_SIZE 8 +PFS_POOL_ENTRY FileSystemStructurePool; + +// +// Declare local procedures +// + +VOID +FiFreeFsStructure( + IN PFILE_SYSTEM_STRUCTURE PFs + ); + +PVOID +FiAllocateFsStructure( + VOID + ); + + +ARC_STATUS +FiGetFileTableEntry( + OUT PULONG Entry + ); + +PFAT_STRUCTURE_CONTEXT +FiAllocateFatStructure( + VOID + ); + + +PVOID +FwAllocatePool( + IN ULONG NumberOfBytes + ); + +VOID +FwStallExecution ( + IN ULONG MicroSeconds + ); + +// +// Static Variables +// + +PCHAR FwPoolBase; +PCHAR FwFreePool; + +// +// Define kernel data used by the Hal. Note that the Hal expects these as +// exported variables, so define pointers. +// + +//ULONG DcacheFlushCount = 0; +//PULONG KeDcacheFlushCount = &DcacheFlushCount; +//ULONG IcacheFlushCount = 0; +//PULONG KeIcacheFlushCount = &IcacheFlushCount; + + +VOID +HalFlushIoBuffers ( + IN PMDL Mdl, + IN BOOLEAN ReadOperation, + IN BOOLEAN DmaOperation + ) + +/*++ + +Routine Description: + + This function flushes the I/O buffer specified by the memory descriptor + list from the data cache on the current processor. + +Arguments: + + Mdl - Supplies a pointer to a memory descriptor list that describes the + I/O buffer location. + + ReadOperation - Supplies a boolean value that determines whether the I/O + operation is a read into memory. + + DmaOperation - Supplies a boolean value that determines whether the I/O + operation is a DMA operation. + +Return Value: + + None. + +--*/ + +{ + + ULONG CacheSegment; + ULONG Length; + ULONG Offset; + KIRQL OldIrql; + PULONG PageFrame; + ULONG Source; + + // + // The Jazz R4000 uses a write back data cache and, therefore, must be + // flushed on reads and writes. + // + // + // If the length of the I/O operation is greater than the size of the + // data cache, then sweep the entire data cache. Otherwise, export or + // purge individual pages from the data cache as appropriate. + // + + Offset = Mdl->ByteOffset & DcacheAlignment; + + Length = (Mdl->ByteCount + + DcacheAlignment + Offset) & ~DcacheAlignment; + + if ((Length > FirstLevelDcacheSize) && + (Length > SecondLevelDcacheSize)) { + + FwSweepDcache(); + FwSweepIcache(); + + } else { + FwSweepDcache(); + FwSweepIcache(); + } + return; +} + + +ARC_STATUS +FwGetFileInformation ( + IN ULONG FileId, + OUT PFILE_INFORMATION Finfo + ) + +/*++ + +Routine Description: + + This function gets the file information for the specified FileId. + +Arguments: + + FileId - Supplies the file table index. + + Finfo - Supplies a pointer to where the File Informatino is stored. + +Return Value: + + If the specified file is open then this routine dispatches to the + File routine. + Otherwise, returns an unsuccessful status. + +--*/ + +{ + + if (BlFileTable[FileId].Flags.Open == 1) { + return (BlFileTable[FileId].DeviceEntryTable->GetFileInformation)(FileId, + Finfo); + } else { + return EACCES; + } +} + +ARC_STATUS +FwSetFileInformation ( + IN ULONG FileId, + IN ULONG AttributeFlags, + IN ULONG AttributeMask + ) + +/*++ + +Routine Description: + + This function sets the file attributes for the specified FileId. + +Arguments: + + FileId - Supplies the file table index. + + AttributeFlags - Supply the attributes to be set for the file. + AttributeMask + +Return Value: + + If the specified file is open and is not a device then this routine + dispatches to the file system routine. + Otherwise, returns an unsuccessful status. + +--*/ + +{ + + if ((BlFileTable[FileId].Flags.Open == 1) && + (BlFileTable[FileId].DeviceId != DEVICE_DEVICE)) { + return (BlFileTable[FileId].DeviceEntryTable->SetFileInformation)(FileId, + AttributeFlags, + AttributeMask); + } else { + return EACCES; + } +} + + +ARC_STATUS +FwRead ( + IN ULONG FileId, + OUT PVOID Buffer, + IN ULONG Length, + OUT PULONG Count + ) + +/*++ + +Routine Description: + + This function reads from a file or a device that is open. + +Arguments: + + FileId - Supplies the file table index. + + Buffer - Supplies a pointer to the buffer that receives the data + read. + + Length - Supplies the number of bytes that are to be read. + + Count - Supplies a pointer to a variable that receives the number of + bytes actually transfered. + +Return Value: + + If the specified file is open for read, then a read is attempted + and the status of the operation is returned. Otherwise, return an + unsuccessful status. + +--*/ + +{ + + // + // If the file is open for read, then attempt to read from it. Otherwise + // return an access error. + // + + if ((BlFileTable[FileId].Flags.Open == 1) && + (BlFileTable[FileId].Flags.Read == 1)) { + return (BlFileTable[FileId].DeviceEntryTable->Read)(FileId, + Buffer, + Length, + Count); + + } else { + return EACCES; + } +} + +ARC_STATUS +FwGetReadStatus ( + IN ULONG FileId + ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ + +{ + + // + // If the file is open for read, then call the call the specific routine. + // Otherwise return an access error. + + if ((BlFileTable[FileId].Flags.Open == 1) && + (BlFileTable[FileId].Flags.Read == 1)) { + + // + // Make sure there is a valid GetReadStatus entry. + // + + if (BlFileTable[FileId].DeviceEntryTable->GetReadStatus != NULL) { + return(BlFileTable[FileId].DeviceEntryTable->GetReadStatus)(FileId); + } else { + return(EACCES); + } + + } else { + return EACCES; + } + + return ESUCCESS; +} + +ARC_STATUS +FwSeek ( + IN ULONG FileId, + IN PLARGE_INTEGER Offset, + IN SEEK_MODE SeekMode + ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + If the specified file is open, then a seek is attempted and + the status of the operation is returned. Otherwise, return an + unsuccessful status. + +--*/ + +{ + + // + // If the file is open, then attempt to seek on it. Otherwise return an + // access error. + // + + if (BlFileTable[FileId].Flags.Open == 1) { + return (BlFileTable[FileId].DeviceEntryTable->Seek)(FileId, + Offset, + SeekMode); + } else { + return EACCES; + } +} + + +ARC_STATUS +FwGetDirectoryEntry ( + IN ULONG FileId, + OUT PDIRECTORY_ENTRY Buffer, + IN ULONG Length, + OUT PULONG Count + ) + +/*++ + +Routine Description: + + This function reads from a file the requested number of directory entries. + +Arguments: + + FileId - Supplies the file table index. + + Buffer - Supplies a pointer to the buffer to receive the directory + entries. + + Length - Supplies the number of directory entries to be read. + + Count - Supplies a pointer to a variable that receives the number of + directory entries actually read.. + +Return Value: + + If the specified file is open for read, then a read is attempted + and the status of the operation is returned. Otherwise, return an + unsuccessful status. + +--*/ +{ + // + // If the file is open for read, then call the call the specific routine. + // Otherwise return an access error. + // + + if ((FileId < BL_FILE_TABLE_SIZE) && + (BlFileTable[FileId].Flags.Open == 1) && + (BlFileTable[FileId].Flags.Read == 1) && + (BlFileTable[FileId].DeviceId != DEVICE_DEVICE)) { + + // + // Check to make sure a GetDirectoryEntry routine exists + // + + if (BlFileTable[FileId].DeviceEntryTable->GetDirectoryEntry != NULL) { + return (BlFileTable[FileId].DeviceEntryTable->GetDirectoryEntry) + (FileId, Buffer, Length, Count); + } + } else { + return EBADF; + } +} + + +ARC_STATUS +FwClose ( + IN ULONG FileId + ) + +/*++ + +Routine Description: + + This function closes a file or a device if it's open. + The DeviceId field indicates if the FileId is a device + (it has the value DEVICE_DEVICE) or is a file. + When closing a file, after the file is closed the + reference counter for the device is decremented and if zero + the device is also closed and the device name removed from + the table of opened devices. + If FileId specifies a device, the reference counter is + decremented and if zero the device is closed and the device + name removed from the table of opened devices. + +Arguments: + + FileId - Supplies the file table index. + +Return Value: + + If the specified file is open, then a close is attempted and + the status of the operation is returned. Otherwise, return an + unsuccessful status. + +--*/ + +{ + ULONG DeviceId; + ARC_STATUS Status; + if (BlFileTable[FileId].Flags.Open == 1) { + // + // Check if closing a file or a device + // + if (BlFileTable[FileId].DeviceId == DEVICE_DEVICE) { + // + // Decrement reference counter, if it's zero call the device + // close routine. + // + OpenedPathTable[FileId].ReferenceCounter--; + if (OpenedPathTable[FileId].ReferenceCounter == 0) { + // + // Remove the name of the device from the table of opened devices. + // + OpenedPathTable[FileId].DeviceName[0] = '\0'; + + // + // Call the device specific close routine. + // + Status = (BlFileTable[FileId].DeviceEntryTable->Close)(FileId); + + // + // If the device has a file system, free the memory used for + // the STRUCTURE_CONTEXT. + // + if (BlFileTable[FileId].StructureContext != NULL) { + FiFreeFsStructure(BlFileTable[FileId].StructureContext); + } + return Status; + } else { + return ESUCCESS; + } + } else { + // + // Close the file + // + DeviceId= BlFileTable[FileId].DeviceId; + Status = (BlFileTable[FileId].DeviceEntryTable->Close)(FileId); + if (Status) { + return Status; + } + + // + // Close also the device + // + return FwClose(DeviceId); + } + } else { + return EACCES; + } +} + +ARC_STATUS +FwOpen ( + IN PCHAR OpenPath, + IN OPEN_MODE OpenMode, + OUT PULONG FileId + ) + +/*++ + +Routine Description: + + This function opens the file specified by OpenPath. + If the device portion of the pathanme is already opened, it reuses + the fid. Otherwise it looks for a driver able to handle this + device and logs the opened device so that it can be reused. + +Arguments: + + OpenPath - ARC compliant pathname of the device/file to be opened. + OpenMode - Supplies the mode in wich the file is opened. + FileId - Pointer to a variable that receives the fid for this + pathname. + +Return Value: + + If the file is successfully opened returns ESUCCESS otherwise + returns an unsuccessfull status. + +--*/ + +{ + ULONG i; + ULONG DeviceId; + PCHAR FileName ; + PCHAR TempString1; + PCHAR TempString2; + ARC_STATUS Status; + CHAR DeviceName[80]; + PVOID TmpStructureContext; + OPEN_MODE DeviceOpenMode; + + // + // Split OpenPath into DeviceName and FileName. + // Search for the last ')' + // + FileName = OpenPath; + for (TempString1 = OpenPath; *TempString1; TempString1++) { + if ( *TempString1 == ')') { + FileName = TempString1+1; + } + } + if (FileName == OpenPath) { + return ENODEV; + } + + // + // Extract the device pathname, convert it to lower case and + // put zeros where the "key" is not specified. + // + TempString1=DeviceName; + for (TempString2=OpenPath;TempString2 != FileName ;TempString2++) { + // + // If about to copy ')' and previous char was '(' + // put a zero in between. + // + if (((*TempString2 == ')') && (*(TempString1-1)) == '(')){ + *TempString1++ = '0'; + } + *TempString1++ = tolower(*TempString2); + } + *TempString1 = '\0'; + + // + // Translate the open mode to its equivalent for devices. + // + DeviceOpenMode = OpenMode; + + if (FileName[0] == '\0') { + // + // On an attempt to open a device with an invalid OpenMode + // return an error. + // + if (OpenMode > ArcOpenReadWrite) { + return EINVAL; + } + } else { + + // + // A file is being open, set the right Open Mode for the device. + // + if (OpenMode > ArcOpenReadOnly) { + DeviceOpenMode = ArcOpenReadWrite; + } + } + + // + // Search for a matching entry in the table of opened devices. + // + for (DeviceId = 0;DeviceId < SIZE_OF_OPENED_PATHNAME_TABLE;DeviceId++) { + if (strcmp(DeviceName,OpenedPathTable[DeviceId].DeviceName)==0) { + // + // device already opened. Check that it's also opened in + // the same mode. + // + if ((DeviceOpenMode != ArcOpenWriteOnly) && (BlFileTable[DeviceId].Flags.Read != 1)) { + continue; + } + if ((DeviceOpenMode != ArcOpenReadOnly) && (BlFileTable[DeviceId].Flags.Write != 1)) { + continue; + } + // + // If opened for the same Mode then just increment reference counter. + // + OpenedPathTable[DeviceId].ReferenceCounter++; + Status = ESUCCESS; + break; + } + } + if (DeviceId == SIZE_OF_OPENED_PATHNAME_TABLE) { + + for (i=0;i < SIZE_OF_LOOKUP_TABLE; i++) { + if (DeviceLookupTable[i].DevicePath == NULL) { + + // + // Driver not found + // + + return ENODEV; + } + if (strstr(DeviceName,DeviceLookupTable[i].DevicePath) == DeviceName) { + + // + // Get a free entry in the file table for the device. + // + + if (Status = FiGetFileTableEntry(&DeviceId)) { + return Status; + } + + // + // Set the dispatch table in the file table. + // + + BlFileTable[DeviceId].DeviceEntryTable = DeviceLookupTable[i].DispatchTable; + break; + } + } + + // + // if end of table, drive not found + // + + if ( i == SIZE_OF_LOOKUP_TABLE ) + { + return ENODEV; + } + + // + // Call the device specific open routine. Use the DeviceName instead of + // the OpenPath so that the drivers always see a lowercase name. + // + + Status = (BlFileTable[DeviceId].DeviceEntryTable->Open)(DeviceName, + DeviceOpenMode, + &DeviceId); + if (Status != ESUCCESS) { + return Status; + } + + // + // if the device was successfully opened. Log this device name + // and initialize the file table. + // + + strcpy(OpenedPathTable[DeviceId].DeviceName,DeviceName); + OpenedPathTable[DeviceId].ReferenceCounter = 1; + + // + // Set flags in file table. + // + + BlFileTable[DeviceId].Flags.Open = 1; + + if (DeviceOpenMode != ArcOpenWriteOnly) { + BlFileTable[DeviceId].Flags.Read = 1; + } + if (DeviceOpenMode != ArcOpenReadOnly) { + BlFileTable[DeviceId].Flags.Write = 1; + } + + // + // Mark this entry in the file table as a device itself. + // + + BlFileTable[DeviceId].DeviceId = DEVICE_DEVICE; + BlFileTable[DeviceId].StructureContext = NULL; + } + + // + // If we get here the device was successfully open and DeviceId contains + // the entry in the file table for this device. + // + + if (FileName[0]) { + + // + // Get an entry for the file. + // + + if (Status=FiGetFileTableEntry(FileId)) { + FwClose( DeviceId ); + return Status; + + } + + // + // Check if the device has a recognized file system on it. If not + // present, allocate a structure context. + // + + if (((TmpStructureContext = BlFileTable[DeviceId].StructureContext) == NULL) && + ((TmpStructureContext = FiAllocateFsStructure()) == NULL)) { + FwClose( DeviceId ); + return EMFILE; + + // + // Check for FAT filesystem. + // + + } + if ((BlFileTable[*FileId].DeviceEntryTable = + IsFatFileStructure(DeviceId,TmpStructureContext)) + != NULL) { + BlFileTable[DeviceId].StructureContext = TmpStructureContext; + //DbgPrint("YES Is fat file structure!!!!!!\n\n"); + + } else { + + FiFreeFsStructure(TmpStructureContext); + FwClose(DeviceId); + //FwPrint("File system not recognized.\r\n"); + return EIO; + } + + // + // Set the DeviceId in the file table. + // + + BlFileTable[*FileId].DeviceId = DeviceId; + + // + // Copy the pointer to FatStructureContext from the device entry + // to the file entry. + // + + BlFileTable[*FileId].StructureContext = BlFileTable[DeviceId].StructureContext; + Status = (BlFileTable[*FileId].DeviceEntryTable->Open)(FileName, + OpenMode, + FileId); + + + // + // If the file could not be opened. Then close the device and + // return the error + // + + if (Status != ESUCCESS) { + FiFreeFsStructure(TmpStructureContext); + FwClose(DeviceId); + return Status; + } + } else { + + // + // No file specified return the fid for the device. + // + *FileId = DeviceId; + return Status; + } +} + +ARC_STATUS +FiGetFileTableEntry( + OUT PULONG Entry + ) + +/*++ + +Routine Description: + + This function looks for an unused entry in the FileTable. + +Arguments: + + Entry - Pointer to the variable that gets an index for the file table. + +Return Value: + + Returns ESUCCESS if a free entry is found + or EMFILE if no entry is available. + +--*/ + +{ + ULONG Index; + for (Index=0;Index < BL_FILE_TABLE_SIZE;Index++) { + if (BlFileTable[Index].Flags.Open == 0) { + *Entry = Index; + return ESUCCESS; + } + } + return EMFILE; +} +ULONG +FiGetFreeLookupEntry ( + VOID + ) + +/*++ + +Routine Description: + + This routine looks for the first available entry in the device + lookup table, that is the entry where DevicePath is NULL. + +Arguments: + + None. + +Return Value: + + Returns the Index of the first free entry of the DeviceLookupTable + or SIZE_OF_LOOKUP_TABLE is the table is full. + + +--*/ + +{ +ULONG Index; + // + // Search for the first free entry in the Lookup table + // + for (Index=0;Index < SIZE_OF_LOOKUP_TABLE;Index++) { + if (DeviceLookupTable[Index].DevicePath == NULL) { + break; + } + } + return Index; +} + +VOID +FwIoInitialize1( + VOID + ) + +/*++ + +Routine Description: + + This routine initializes the file table used by the firmware to + export I/O functions to client programs loaded from the system + partition, initializes the I/O entry points in the firmware + transfer vector and initializes the display driver. + + Note: This routine is caleld at phase 1 initialization. + +Arguments: + + None. + +Return Value: + + None. + +--*/ + +{ + + ULONG Index; + // + // Initialize the system parameter block. + // + + SYSTEM_BLOCK->Signature = 0x53435241; + SYSTEM_BLOCK->Length = sizeof(SYSTEM_PARAMETER_BLOCK); + SYSTEM_BLOCK->Version = ARC_VERSION; + SYSTEM_BLOCK->Revision = ARC_REVISION; + SYSTEM_BLOCK->RestartBlock = NULL; + SYSTEM_BLOCK->DebugBlock = NULL; + SYSTEM_BLOCK->FirmwareVector = (PVOID)((PUCHAR)SYSTEM_BLOCK + sizeof(SYSTEM_PARAMETER_BLOCK)); + + SYSTEM_BLOCK->FirmwareVectorLength = (ULONG)MaximumRoutine * sizeof(ULONG); + SYSTEM_BLOCK->VendorVectorLength = 0; + + // + // Initialize the I/O entry points in the firmware transfer vector. + // + + (PARC_CLOSE_ROUTINE)SYSTEM_BLOCK->FirmwareVector[CloseRoutine] = FwClose; + (PARC_MOUNT_ROUTINE)SYSTEM_BLOCK->FirmwareVector[MountRoutine] = NULL; + (PARC_OPEN_ROUTINE)SYSTEM_BLOCK->FirmwareVector[OpenRoutine] = FwOpen; + (PARC_READ_ROUTINE)SYSTEM_BLOCK->FirmwareVector[ReadRoutine] = FwRead; + (PARC_READ_STATUS_ROUTINE)SYSTEM_BLOCK->FirmwareVector[ReadStatusRoutine] = + FwGetReadStatus; + (PARC_SEEK_ROUTINE)SYSTEM_BLOCK->FirmwareVector[SeekRoutine] = FwSeek; + (PARC_WRITE_ROUTINE)SYSTEM_BLOCK->FirmwareVector[WriteRoutine] = NULL; + (PARC_GET_FILE_INFO_ROUTINE)SYSTEM_BLOCK->FirmwareVector[GetFileInformationRoutine] = FwGetFileInformation; + (PARC_SET_FILE_INFO_ROUTINE)SYSTEM_BLOCK->FirmwareVector[SetFileInformationRoutine] = FwSetFileInformation; + (PARC_GET_DIRECTORY_ENTRY_ROUTINE)SYSTEM_BLOCK->FirmwareVector[GetDirectoryEntryRoutine] = FwGetDirectoryEntry; + + // + // Initialize the file table. + // + + for (Index = 0; Index < BL_FILE_TABLE_SIZE; Index += 1) { + BlFileTable[Index].Flags.Open = 0; + } + + // + // Initialize the driver lookup table. + // + for (Index=0;Index < SIZE_OF_LOOKUP_TABLE;Index++) { + DeviceLookupTable[Index].DevicePath = NULL; + } + + // + // Initialize the table of opened devices. + // + for (Index = 0;Index < SIZE_OF_OPENED_PATHNAME_TABLE;Index++) { + OpenedPathTable[Index].DeviceName[0]='\0'; + } + + return; +} + +VOID +FwIoInitialize2( + VOID + ) + +/*++ + +Routine Description: + + This routine calls the device driver initialization routines. + +Arguments: + + None. + +Return Value: + + None. + +--*/ +{ + ULONG Index; + + // + // Call the mini-port driver initialization routine. + // + + DriverEntry(NULL); + + // + // Call the scsi driver initialization routine + // + if ((Index=FiGetFreeLookupEntry()) == SIZE_OF_LOOKUP_TABLE) { + //FwPrint(NotEnoughEntriesMsg); + } else { + HardDiskInitialize(&DeviceLookupTable[Index], + SIZE_OF_LOOKUP_TABLE-Index); + } + + // + // Pre allocate memory for the File system structures. + // + + FileSystemStructurePool = + FwAllocatePool(sizeof(FS_POOL_ENTRY) * FS_POOL_SIZE); + + return; +} + +PVOID +FiAllocateFsStructure( + VOID + ) + +/*++ + +Routine Description: + + This routine allocates a File System structure + +Arguments: + + None. + +Return Value: + + Returns a pointer to the Allocated File System structure or NULL. + +--*/ + +{ + + PFS_POOL_ENTRY TmpPointer,Last; + + TmpPointer = FileSystemStructurePool; + + Last = FileSystemStructurePool+FS_POOL_SIZE; + do { + if (TmpPointer->InUse == FALSE) { + TmpPointer->InUse = TRUE; + return &TmpPointer->Fs; + } + TmpPointer++; + } while (TmpPointer != Last); + return NULL; +} +VOID +FiFreeFsStructure( + IN PFILE_SYSTEM_STRUCTURE PFs + ) + +/*++ + +Routine Description: + + This routine frees a File System structure previously allocated. + +Arguments: + + PFs pointer to the file system structure to free. + +Return Value: + + None. + +--*/ + +{ + CONTAINING_RECORD(PFs, FS_POOL_ENTRY, Fs)->InUse = FALSE; + return; +} + + +PVOID +FwAllocatePool( + IN ULONG NumberOfBytes + ) + +/*++ + +Routine Description: + + This routine allocates the requested number of bytes from the firmware + pool. If enough pool exists to satisfy the request, a pointer to the + next free cache-aligned block is returned, otherwise NULL is returned. + The pool is zeroed at initialization time, and no corresponding + "FwFreePool" routine exists. + +Arguments: + + NumberOfBytes - Supplies the number of bytes to allocate. + +Return Value: + + NULL - Not enough pool exists to satisfy the request. + + NON-NULL - Returns a pointer to the allocated pool. + +--*/ + +{ + PVOID Pool; + + // + // If there is not enough free pool for this request or the requested + // number of bytes is zero, return NULL, otherwise return a pointer to + // the free block and update the free pointer. + // + + if (((FwFreePool + NumberOfBytes) > (FwPoolBase + FW_POOL_SIZE)) || + (NumberOfBytes == 0)) { + + Pool = NULL; + + } else { + + Pool = FwFreePool; + + // + // Move pointer to the next cache aligned section of free pool. + // + + FwFreePool += ((NumberOfBytes - 1) & ~(KeGetDcacheFillSize() - 1)) + + KeGetDcacheFillSize(); + } + return Pool; +} + +VOID +FwStallExecution ( + IN ULONG MicroSeconds + ) + +/*++ + +Routine Description: + + This function stalls execution for the specified number of microseconds. + +Arguments: + + MicroSeconds - Supplies the number of microseconds that execution is to be + stalled. + +Return Value: + + None. + +--*/ + +{ + + ULONG Index; + ULONG Limit; + PULONG Store; + ULONG Value; + + // + // ****** begin temporary code ****** + // + // This code must be replaced with a smarter version. For now it assumes + // an execution rate of 50,000,000 instructions per second and 4 instructions + // per iteration. + // + + Store = &Value; + Limit = (MicroSeconds * 50 / 4); + for (Index = 0; Index < Limit; Index += 1) { + *Store = Index; + } + return; +} + +BOOLEAN +FwGetPathMnemonicKey( + IN PCHAR OpenPath, + IN PCHAR Mnemonic, + IN PULONG Key + ) + +/*++ + +Routine Description: + + This routine looks for the given Mnemonic in OpenPath. + If Mnemonic is a component of the path, then it converts the key + value to an integer wich is returned in Key. + +Arguments: + + OpenPath - Pointer to a string that contains an ARC pathname. + + Mnemonic - Pointer to a string that contains a ARC Mnemonic + + Key - Pointer to a ULONG where the Key value is stored. + + +Return Value: + + FALSE if mnemonic is found in path and a valid key is converted. + TRUE otherwise. + +--*/ + +{ + + PCHAR Tmp; + CHAR Digits[4]; + ULONG i; + CHAR String[16]; + + // + // Construct a string of the form ")mnemonic(" + // + String[0]=')'; + for(i=1;*Mnemonic;i++) { + String[i] = * Mnemonic++; + } + String[i++]='('; + String[i]='\0'; + + if ((Tmp=strstr(OpenPath,&String[1])) == NULL) { + return TRUE; + } + + if (Tmp != OpenPath) { + if ((Tmp=strstr(OpenPath,String)) == NULL) { + return TRUE; + } + } else { + i--; + } + // + // skip the mnemonic and convert the value in between parentesis to integer + // + Tmp+=i; + for (i=0;i<3;i++) { + if (*Tmp == ')') { + Digits[i] = '\0'; + break; + } + Digits[i] = *Tmp++; + } + Digits[i]='\0'; + *Key = atoi(Digits); + return FALSE; +} + +ULONG +FwPrint ( + PCHAR Format, + ... + ) +{ +} diff --git a/private/ntos/fw/duobase/mips/fwload.c b/private/ntos/fw/duobase/mips/fwload.c new file mode 100644 index 000000000..cd80d3733 --- /dev/null +++ b/private/ntos/fw/duobase/mips/fwload.c @@ -0,0 +1,301 @@ +/*++ + +Copyright (c) 1991 Microsoft Corporation + +Module Name: + + fwload.c + +Abstract: + + This module implements the ARC software loadable functions. + +Author: + + Lluis Abello (lluis) 19-Sep-1991 + +Environment: + + Kernel mode only. + +Revision History: + +--*/ + +#include "fwp.h" +#include "string.h" +#include "ntimage.h" +#include "duobase.h" + + +// +// s_flags values +// + +#define STYP_REG 0x00000000 +#define STYP_TEXT 0x00000020 +#define STYP_INIT 0x80000000 +#define STYP_RDATA 0x00000100 +#define STYP_DATA 0x00000040 +#define STYP_LIT8 0x08000000 +#define STYP_LIT4 0x10000000 +#define STYP_SDATA 0x00000200 +#define STYP_SBSS 0x00000080 +#define STYP_BSS 0x00000400 +#define STYP_LIB 0x40000000 +#define STYP_UCODE 0x00000800 +#define S_NRELOC_OVFL 0x20000000 + +ULONG FwActualBasePage; +ULONG FwPageCount; + + +ARC_STATUS +FwLoad ( + IN PCHAR ImagePath, + IN ULONG TopAddress, + OUT PULONG EntryAddress, + OUT PULONG LowAddress + ) + +/*++ + +Routine Description: + + This routine attempts to load the specified file from the specified + device. + +Arguments: + + ImagePath - Supplies a pointer to the path of the file to load. + + TopAddress - Supplies the top address of a region of memory into which + the file is to be loaded. + + EntryAddress - Supplies a pointer to a variable to receive the entry point + of the image, if defined. + + LowAddress - Supplies a pointer to a variable to receive the low address + of the loaded file. + +Return Value: + + ESUCCESS is returned if the specified image file is loaded + successfully. Otherwise, an unsuccessful status is returned + that describes the reason for failure. + +--*/ + +{ + ULONG ActualBase; + ULONG SectionBase; + ULONG SectionOffset; + ULONG SectionIndex; + ULONG Count; + PIMAGE_FILE_HEADER FileHeader; + ULONG FileId; + ULONG Index; + UCHAR LocalBuffer[SECTOR_SIZE+128]; + PUCHAR LocalPointer; + ULONG NumberOfSections; + PFW_MEMORY_DESCRIPTOR FwMemoryDescriptor; + PMEMORY_DESCRIPTOR MemoryDescriptor; + PIMAGE_OPTIONAL_HEADER OptionalHeader; + PIMAGE_SECTION_HEADER SectionHeader; + ARC_STATUS Status; + LARGE_INTEGER SeekPosition; + ULONG SectionFlags; + + // + // Align the buffer on a Dcache line size. + // + + LocalPointer = (PVOID) ((ULONG) ((PCHAR) LocalBuffer + KeGetDcacheFillSize() - 1) + & ~(KeGetDcacheFillSize() - 1)); + + // + // Set the image start address to null. + // + + *EntryAddress = 0; + + // + // Attempt to open the load file. + // + + Status = FwOpen(ImagePath, ArcOpenReadOnly, &FileId); + if (Status != ESUCCESS) { + return Status; + } + + // + // Read the image header from the file. + // + + Status = FwRead(FileId, LocalPointer, SECTOR_SIZE, &Count); + if (Status != ESUCCESS) { + FwClose(FileId); + return Status; + } + + // + // Get a pointer to the file header and begin processing it. + // + + FileHeader = (PIMAGE_FILE_HEADER)LocalPointer; + OptionalHeader = + (PIMAGE_OPTIONAL_HEADER)(LocalPointer + sizeof(IMAGE_FILE_HEADER)); + SectionHeader = + (PIMAGE_SECTION_HEADER)(LocalPointer + sizeof(IMAGE_FILE_HEADER) + + FileHeader->SizeOfOptionalHeader); + + // + // If the image file is not the specified type, then return bad image + // type status. + // + + if (!((FileHeader->Machine == IMAGE_FILE_MACHINE_R3000) || + (FileHeader->Machine == IMAGE_FILE_MACHINE_R4000)) || + ((FileHeader->Characteristics & IMAGE_FILE_EXECUTABLE_IMAGE) == 0)) { + FwClose(FileId); + ScsiDebugPrint(1,"Bad File Header\n"); + return EBADF; + } + + // + // If the image cannot be relocated, set the ActualBase to the code base, + // and compute the image size by subtracting the code base from the data + // base plus the size of the data. If the image can be relocated, + // set ActualBase to the TopAddress minus the image size, and compute the + // image size by adding the size of the code, initialized data, and + // uninitialized data. + // + + NumberOfSections = FileHeader->NumberOfSections; + + if ((FileHeader->Characteristics & IMAGE_FILE_RELOCS_STRIPPED) != 0) { + ActualBase = OptionalHeader->BaseOfCode; + FwPageCount = (OptionalHeader->BaseOfData + OptionalHeader->SizeOfInitializedData) - + ActualBase; + } else { + ScsiDebugPrint(1,"Relocatable file not supported\n"); + FwClose(FileId); + return EBADF; + } + + // + // Check That the program is linked at the right place + // + if (((ActualBase+FwPageCount) & ~KSEG1_BASE) > (TopAddress & ~KSEG1_BASE)) { + FwClose(FileId); + return EBADF; + } + + // + // Convert ActualBasePage and PageCount to be in units of pages instead of + // bytes. + // + + FwActualBasePage = (ActualBase & 0x1fffffff) >> PAGE_SHIFT; + + if (strcmp((PCHAR)&SectionHeader[NumberOfSections - 1].Name, ".debug") == 0) { + NumberOfSections -= 1; + FwPageCount -= SectionHeader[NumberOfSections].SizeOfRawData; + } + + FwPageCount = (FwPageCount + PAGE_SIZE - 1) >> PAGE_SHIFT; + + *LowAddress = ActualBase | KSEG0_BASE; + + // + // Return the entry address to the caller. + // + + *EntryAddress = ((ActualBase | KSEG0_BASE) + + (OptionalHeader->AddressOfEntryPoint - OptionalHeader->BaseOfCode) + ); + + + // + // Scan through the sections and either read them into memory or clear + // the memory as appropriate. + // + + SectionOffset = 0; + for (Index = 0; Index < NumberOfSections; Index += 1) { + + // + // Compute the destination address for the current section. + // + SectionBase = SectionHeader->VirtualAddress | KSEG0_BASE; + + // + // If the section is code, initialized data, or other, then read + // the code or data into memory. + // + + if ((SectionHeader->Characteristics & + (STYP_TEXT | STYP_INIT | STYP_RDATA | STYP_DATA | STYP_SDATA)) != 0) { + + SeekPosition.LowPart = SectionHeader->PointerToRawData; + SeekPosition.HighPart = 0; + Status = FwSeek(FileId, + &SeekPosition, + SeekAbsolute); + + if (Status != ESUCCESS) { + break; + } + + Status = FwRead(FileId, + (PVOID)SectionBase, + SectionHeader->SizeOfRawData, + &Count); + + if (Status != ESUCCESS) { + ScsiDebugPrint(1,"Section read error %lx\n",Status); + break; + } + + // + // Set the offset of the next section + // + SectionOffset += SectionHeader->SizeOfRawData; + + // + // If the section is uninitialized data, then zero the specifed memory. + // + + } else if ((SectionHeader->Characteristics & (STYP_BSS | STYP_SBSS)) != 0) { + + RtlZeroMemory((PVOID)(SectionBase), SectionHeader->SizeOfRawData); + + // + // Set the offset of the next section + // + + SectionOffset += SectionHeader->SizeOfRawData; + + } + + SectionHeader += 1; + ScsiDebugPrint(2,"Section successfully read\n"); + } + + + // + // Close file and return completion status. + // + FwClose(FileId); + if (Status == ESUCCESS) { + + // + // Flush the instruction and data caches. + // + + FwSweepIcache(); + FwSweepDcache(); + + } + return Status; +} diff --git a/private/ntos/fw/duobase/mips/fwp.h b/private/ntos/fw/duobase/mips/fwp.h new file mode 100644 index 000000000..23e85b28e --- /dev/null +++ b/private/ntos/fw/duobase/mips/fwp.h @@ -0,0 +1,633 @@ +/*++ + +Copyright (c) 1991 Microsoft Corporation + +Module Name: + + fwp.h + +Abstract: + + This module contains extensions to the firmware.h header file. + +Author: + + David M. Robinson (davidro) 29-Aug-1991 + +Revision History: + +--*/ + +#ifndef _FWP_ +#define _FWP_ + +#include "bldr.h" +#include "firmware.h" +#include "..\mips\kbdmouse.h" +#ifdef DUO +#include "duodef.h" +#include "duoprom.h" +#include "duodma.h" +#else +#include "jazzdef.h" +#include "jazzprom.h" +#include "jazzdma.h" +#endif + +extern ULONG FirstLevelDcacheSize; +extern ULONG FirstLevelDcacheFillSize; +extern ULONG SecondLevelDcacheSize; +extern ULONG SecondLevelDcacheFillSize; +extern ULONG DcacheFillSize; +extern ULONG DcacheAlignment; +extern ULONG FirstLevelIcacheSize; +extern ULONG FirstLevelIcacheFillSize; +extern ULONG SecondLevelIcacheSize; +extern ULONG SecondLevelIcacheFillSize; + + + +#undef KeGetDcacheFillSize +#define KeGetDcacheFillSize() DcacheFillSize + +// +// TEMPTEMP Temporary defines. +// + +#define SECONDARY_CACHE_SIZE (1 << 20) +#define SECONDARY_CACHE_INVALID 0x0 +#define SECONDARY_CACHE_DIRTY_EXCLUSIVE 0x5 +#define TAGLO_SSTATE 0xA + + +// +// Current version and revision numbers. +// + +#define ARC_VERSION 1 +#define ARC_REVISION 2 + + + +// +// Define the firmware vendor specific entry point numbers. +// + +typedef enum _VENDOR_ENTRY { + AllocatePoolRoutine, + StallExecutionRoutine, + PrintRoutine, + SetDisplayAttributesRoutine, + OutputCharacterRoutine, + ScrollVideoRoutine, + MaximumVendorRoutine + } VENDOR_ENTRY; + +// +// Define vendor specific routine types. +// + +typedef +PVOID +(*PVEN_ALLOCATE_POOL_ROUTINE) ( + IN ULONG NumberOfBytes + ); + +typedef +VOID +(*PVEN_STALL_EXECUTION_ROUTINE) ( + IN ULONG Microseconds + ); + +typedef +ULONG +(*PVEN_PRINT_ROUTINE) ( + IN PCHAR Format, + ... + ); + +typedef +VOID +(*PVEN_SET_DISPLAY_ATTRIBUTES_ROUTINE) ( + IN ULONG ForegroundColor, + IN ULONG BackgroundColor, + IN BOOLEAN HighIntensity, + IN BOOLEAN Underscored, + IN BOOLEAN ReverseVideo, + IN ULONG CharacterWidth, + IN ULONG CharacterHeight + ); + +typedef +VOID +(*PVEN_OUTPUT_CHARACTER_ROUTINE) ( + IN PVOID Character, + IN ULONG Row, + IN ULONG Column + ); + +typedef +VOID +(*PVEN_SCROLL_VIDEO_ROUTINE) ( + VOID + ); + +// +// Define vendor specific prototypes. +// + +PVOID +FwAllocatePool ( + IN ULONG NumberOfBytes + ); + +VOID +FwStallExecution ( + IN ULONG Microseconds + ); + +ULONG +FwPrint ( + IN PCHAR Format, + ... + ); + +VOID +FwSetDisplayAttributes ( + IN ULONG ForegroundColor, + IN ULONG BackgroundColor, + IN BOOLEAN HighIntensity, + IN BOOLEAN Underscored, + IN BOOLEAN ReverseVideo, + IN ULONG CharacterWidth, + IN ULONG CharacterHeight + ); + +VOID +FwOutputCharacter ( + IN PVOID Character, + IN ULONG Row, + IN ULONG Column + ); + +VOID +FwScrollVideo ( + VOID + ); + + +// +// Define the Lookup table. At initialization, the driver must fill this table +// with the device pathnames it can handle. +// + +typedef struct _DRIVER_LOOKUP_ENTRY { + PCHAR DevicePath; + PBL_DEVICE_ENTRY_TABLE DispatchTable; +} DRIVER_LOOKUP_ENTRY, *PDRIVER_LOOKUP_ENTRY; + +#define SIZE_OF_LOOKUP_TABLE BL_FILE_TABLE_SIZE + +extern DRIVER_LOOKUP_ENTRY DeviceLookupTable[SIZE_OF_LOOKUP_TABLE]; + +// +// Define the Device Pathname. This table is indexed with the FileId. +// FwOpen tries to match the OpenPath with the entries in this table, and +// if it finds a match it increments the reference counter. If it doesn't +// find a match it tries to match an entry in the DRIVER_LOOKUP_TABLE +// and then calls the Open routine of that driver. +// + +#define SIZE_OF_ARC_DEVICENAME 64 + +typedef struct _OPENED_PATHNAME_ENTRY { + ULONG ReferenceCounter; + CHAR DeviceName[SIZE_OF_ARC_DEVICENAME]; +} OPENED_PATHNAME_ENTRY, *POPENED_PATHNAME_ENTRY; + +#define SIZE_OF_OPENED_PATHNAME_TABLE BL_FILE_TABLE_SIZE + +extern OPENED_PATHNAME_ENTRY OpenedPathTable[SIZE_OF_OPENED_PATHNAME_TABLE]; + +// +// Driver initialization routines. +// + +VOID +FwInitializeMemory( + IN VOID + ); + +VOID +FwResetMemory( + IN VOID + ); + +VOID +DisplayInitialize( + IN OUT PDRIVER_LOOKUP_ENTRY LookupTableEntry, + IN ULONG Entries + ); + +VOID +KeyboardInitialize( + IN OUT PDRIVER_LOOKUP_ENTRY LookupTableEntry, + IN ULONG Entries + ); + +VOID +SerialInitialize( + IN OUT PDRIVER_LOOKUP_ENTRY LookupTableEntry, + IN ULONG Entries + ); + +VOID +HardDiskInitialize( + IN OUT PDRIVER_LOOKUP_ENTRY LookupTable, + IN ULONG Entries + ); + +VOID +FloppyInitialize( + IN OUT PDRIVER_LOOKUP_ENTRY LookupTableEntry, + IN ULONG Entries + ); + + + +// +// Define the private configuration packet structure, which contains a +// configuration component as well as pointers to the component's parent, +// peer, child, and configuration data. +// + +typedef struct _CONFIGURATION_PACKET { + CONFIGURATION_COMPONENT Component; + struct _CONFIGURATION_PACKET *Parent; + struct _CONFIGURATION_PACKET *Peer; + struct _CONFIGURATION_PACKET *Child; + PVOID ConfigurationData; +} CONFIGURATION_PACKET, *PCONFIGURATION_PACKET; + +// +// The compressed configuration packet structure used to store configuration +// data in NVRAM. +// + +typedef struct _COMPRESSED_CONFIGURATION_PACKET { + UCHAR Parent; + UCHAR Class; + UCHAR Type; + UCHAR Flags; + ULONG Key; + UCHAR Version; + UCHAR Revision; + USHORT ConfigurationDataLength; + USHORT Identifier; + USHORT ConfigurationData; +} COMPRESSED_CONFIGURATION_PACKET, *PCOMPRESSED_CONFIGURATION_PACKET; + +// +// Defines for Identifier index. +// + +#define NO_CONFIGURATION_IDENTIFIER 0xFFFF + +// +// Defines for the volatile and non-volatile configuration tables. +// + +#define NUMBER_OF_ENTRIES 40 +#define LENGTH_OF_IDENTIFIER (1024 - (40*16) - 8) +#define LENGTH_OF_DATA 2048 +#define LENGTH_OF_ENVIRONMENT 1024 +#define LENGTH_OF_EISA_DATA 2044 + +#define MAXIMUM_ENVIRONMENT_VALUE 256 + +// +// The volatile configuration table structure. +// + +typedef struct _CONFIGURATION { + CONFIGURATION_PACKET Packet[NUMBER_OF_ENTRIES]; + UCHAR Identifier[LENGTH_OF_IDENTIFIER]; + UCHAR Data[LENGTH_OF_DATA]; + UCHAR EisaData[LENGTH_OF_EISA_DATA]; +} CONFIGURATION, *PCONFIGURATION; + +// +// The non-volatile configuration table structure. +// + +typedef struct _NV_CONFIGURATION { + + // + // First Page + // + + COMPRESSED_CONFIGURATION_PACKET Packet[NUMBER_OF_ENTRIES]; + UCHAR Identifier[LENGTH_OF_IDENTIFIER]; + UCHAR Data[LENGTH_OF_DATA]; + UCHAR Checksum1[4]; + UCHAR Environment[LENGTH_OF_ENVIRONMENT]; + UCHAR Checksum2[4]; + + // + // Second Page + // + + UCHAR EisaData[LENGTH_OF_EISA_DATA]; + UCHAR Checksum3[4]; + +} NV_CONFIGURATION, *PNV_CONFIGURATION; + +// +// Define identifier index, data index, pointer to configuration table. +// + +extern ULONG IdentifierIndex; +extern ULONG DataIndex; +extern ULONG EisaDataIndex; +extern PCONFIGURATION Configuration; + +// +// Non-volatile ram layout. +// + +#define NVRAM_CONFIGURATION NVRAM_VIRTUAL_BASE +#define NVRAM_SYSTEM_ID (NVRAM_VIRTUAL_BASE + 0x00002000) + +// +// Memory size. The MctadrRev2 is used to interpret the memory size value +// in the configuration register. +// + +extern ULONG MemorySize; +#define MEMORY_SIZE (MemorySize << 20) +extern BOOLEAN MctadrRev2; + +// +// Memory layout. +// + +#define FW_POOL_BASE 0xA0100000 +#define FW_POOL_SIZE 0xf000 + +// +// Define special character values. TEMPTEMP These should go somewhere else. +// + +#define ASCII_NUL 0x00 +#define ASCII_BEL 0x07 +#define ASCII_BS 0x08 +#define ASCII_HT 0x09 +#define ASCII_LF 0x0A +#define ASCII_VT 0x0B +#define ASCII_FF 0x0C +#define ASCII_CR 0x0D +#define ASCII_CSI 0x9B +#define ASCII_ESC 0x1B +#define ASCII_SYSRQ 0x80 + +// +// Define screen colors. +// + +typedef enum _ARC_SCREEN_COLOR { + ArcColorBlack, + ArcColorRed, + ArcColorGreen, + ArcColorYellow, + ArcColorBlue, + ArcColorMagenta, + ArcColorCyan, + ArcColorWhite, + MaximumArcColor + } ARC_SCREEN_COLOR; + +// +// Define video board types for Jazz. +// + +typedef enum _JAZZ_VIDEO_TYPE { + JazzVideoG300, + JazzVideoG364, + JazzVideoVxl, + Reserved3, + Reserved4, + Reserved5, + Reserved6, + Reserved7, + Reserved8, + Reserved9, + ReservedA, + ReservedB, + ReservedC, + ReservedD, + ReservedE, + ReservedF, + MipsVideoG364, + MaximumJazzVideo + } JAZZ_VIDEO_TYPE, *PJAZZ_VIDEO_TYPE; + + + +// +// Define firmware routine prototypes. +// + +VOID +FwIoInitialize1 ( + VOID + ); + +VOID +FwIoInitialize2 ( + VOID + ); + +BOOLEAN +FwGetPathMnemonicKey( + IN PCHAR OpenPath, + IN PCHAR Mnemonic, + OUT PULONG Key + ); + +PCHAR +FwEnvironmentLoad( + VOID + ); + +VOID +FwPrintVersion ( + VOID + ); + +ARC_STATUS +DisplayBootInitialize( + VOID + ); + +ARC_STATUS +FwGetVideoData ( + OUT PMONITOR_CONFIGURATION_DATA MonitorData + ); + +VOID +FwSetVideoData ( + IN PMONITOR_CONFIGURATION_DATA MonitorData + ); + +VOID +FwTerminationInitialize( + IN VOID + ); + +VOID +FwHalt( + IN VOID + ); + +VOID +FwMonitor( + IN ULONG + ); + +VOID +FwExceptionInitialize( + IN VOID + ); + +VOID +ResetSystem ( + IN VOID + ); + + +VOID +FwpFreeStub( + IN PVOID Buffer + ); + +typedef enum _GETSTRING_ACTION { + GetStringSuccess, + GetStringEscape, + GetStringUpArrow, + GetStringDownArrow, + GetStringMaximum +} GETSTRING_ACTION, *PGETSTRING_ACTION; + +GETSTRING_ACTION +FwGetString( + OUT PCHAR String, + IN ULONG StringLength, + IN PCHAR InitialString OPTIONAL, + IN ULONG CurrentRow, + IN ULONG CurrentColumn + ); + +ARC_STATUS +FwConfigurationCheckChecksum ( + VOID + ); + +ARC_STATUS +FwEnvironmentCheckChecksum ( + VOID + ); + +VOID +FwpReservedRoutine( + VOID + ); + +VOID +FwWaitForKeypress( + VOID + ); + +VOID +JzShowTime ( + BOOLEAN First + ); + +VOID +JxBmp ( + VOID + ); + +ULONG +JxDisplayMenu ( + IN PCHAR Choices[], + IN ULONG NumberOfChoices, + IN LONG DefaultChoice, + IN ULONG CurrentLine + ); + +BOOLEAN +FwGetVariableSegment ( + IN ULONG SegmentNumber, + IN OUT PCHAR Segment + ); + +ARC_STATUS +FwSetVariableSegment ( + IN ULONG SegmentNumber, + IN PCHAR VariableName, + IN OUT PCHAR Segment + ); + +// +// Print macros. +// + +extern BOOLEAN DisplayOutput; +extern BOOLEAN SerialOutput; +extern BOOLEAN FwConsoleInitialized; +extern BOOLEAN SetupIsRunning; + +ULONG +FwPrint ( + PCHAR Format, + ... + ); + +#define FwClearScreen() \ + FwPrint("%c2J", ASCII_CSI) + +#define FwSetScreenColor(FgColor, BgColor) \ + FwPrint("%c3%dm", ASCII_CSI, (UCHAR)FgColor); \ + FwPrint("%c4%dm", ASCII_CSI, (UCHAR)BgColor) + +#define FwSetScreenAttributes( HighIntensity, Underscored, ReverseVideo ) \ + FwPrint("%c0m", ASCII_CSI); \ + if (HighIntensity) { \ + FwPrint("%c1m", ASCII_CSI); \ + } \ + if (Underscored) { \ + FwPrint("%c4m", ASCII_CSI); \ + } \ + if (ReverseVideo) { \ + FwPrint("%c7m", ASCII_CSI); \ + } + +#define FwSetPosition( Row, Column ) \ + FwPrint("%c%d;%dH", ASCII_CSI, (Row + 1), (Column + 1)) + +#define FwClearLine() \ + FwPrint ("%c2K",ASCII_CSI) + +#define FwMoveCursorLeft(Spaces) \ + FwPrint ("%c%dD", ASCII_CSI, Spaces) + +#define FwMoveCursorToColumn(Spaces) \ + FwPrint( "\r" ); \ + if ( Spaces > 1 ) \ + FwPrint( "%c%dC", ASCII_CSI, Spaces - 1) + + + +#define KeFlushIoBuffers(Mdl, Read, Dma) HalFlushIoBuffers(Mdl, Read, Dma) + +#endif // _FWP_ diff --git a/private/ntos/fw/duobase/mips/jxdisp.c b/private/ntos/fw/duobase/mips/jxdisp.c new file mode 100644 index 000000000..9ced20813 --- /dev/null +++ b/private/ntos/fw/duobase/mips/jxdisp.c @@ -0,0 +1,2086 @@ +#if defined(JAZZ) + +/*++ + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + jxdisp.c + +Abstract: + + This module implements the video boot driver for the Jazz system. + +Author: + + David M. Robinson (davidro) 24-Jul-1991 + +Environment: + + Kernel mode. + + +Revision History: + +--*/ + +#include "fwp.h" +#include "jazzvdeo.h" +#include "jxvideo.h" +#include "selftest.h" +#include "string.h" +#include "duobase.h" +#include "duoreset.h" + +ARC_STATUS +InitializeG300 ( + IN PMONITOR_CONFIGURATION_DATA GlobalMonitor + ); + +ARC_STATUS +InitializeG364 ( + IN PMONITOR_CONFIGURATION_DATA GlobalMonitor + ); + +VOID +FillVideoMemory ( + IN PUCHAR StartAddress, + IN ULONG SizeInBytes, + IN ULONG Pattern + ); + +ARC_STATUS +DisplayClose ( + IN ULONG FileId + ); + +ARC_STATUS +DisplayMount ( + IN PCHAR MountPath, + IN MOUNT_OPERATION Operation + ); + +ARC_STATUS +DisplayOpen ( + IN PCHAR OpenPath, + IN OPEN_MODE OpenMode, + IN OUT PULONG FileId + ); + +ARC_STATUS +DisplayRead ( + IN ULONG FileId, + IN PVOID Buffer, + IN ULONG Length, + OUT PULONG Count + ); + +ARC_STATUS +DisplayGetReadStatus ( + IN ULONG FileId + ); + +ARC_STATUS +DisplayWrite ( + IN PVOID Buffer, + IN ULONG Length, + OUT PULONG Count + ); + +ARC_STATUS +DisplayGetFileInformation ( + IN ULONG FileId, + OUT PFILE_INFORMATION Finfo + ); + +VOID FwVideoScroll( + PVOID StartAddress, + PVOID EndAddress, + PVOID Destination + ); + +#define G300_PALETTE_BLACK 0x000000 +#define G300_PALETTE_RED 0x0000B0 +#define G300_PALETTE_GREEN 0x00B000 +#define G300_PALETTE_YELLOW 0x00B0B0 +#define G300_PALETTE_BLUE 0x900000 +#define G300_PALETTE_MAGENTA 0xB000B0 +#define G300_PALETTE_CYAN 0xB0B000 +#define G300_PALETTE_WHITE 0xB0B0B0 +#define G300_PALETTE_HI_BLACK 0x000000 +#define G300_PALETTE_HI_RED 0x0000FF +#define G300_PALETTE_HI_GREEN 0x00FF00 +#define G300_PALETTE_HI_YELLOW 0x00FFFF +#define G300_PALETTE_HI_BLUE 0xFF0000 +#define G300_PALETTE_HI_MAGENTA 0xFF00FF +#define G300_PALETTE_HI_CYAN 0xFFFF00 +#define G300_PALETTE_HI_WHITE 0xFFFFFF + +#define G364_PALETTE_BLACK 0x000000 +#define G364_PALETTE_RED 0xB00000 +#define G364_PALETTE_GREEN 0x00B000 +#define G364_PALETTE_YELLOW 0xB0B000 +#define G364_PALETTE_BLUE 0x0000B0 +#define G364_PALETTE_MAGENTA 0xB000B0 +#define G364_PALETTE_CYAN 0x00B0B0 +#define G364_PALETTE_WHITE 0xB0B0B0 +#define G364_PALETTE_HI_BLACK 0x000000 +#define G364_PALETTE_HI_RED 0xFF0000 +#define G364_PALETTE_HI_GREEN 0x00FF00 +#define G364_PALETTE_HI_YELLOW 0xFFFF00 +#define G364_PALETTE_HI_BLUE 0x0000FF +#define G364_PALETTE_HI_MAGENTA 0xFF00FF +#define G364_PALETTE_HI_CYAN 0x00FFFF +#define G364_PALETTE_HI_WHITE 0xFFFFFF + +// +// Define virtual address of the video memory and control registers. +// +#define VIDEO_MEMORY ((PUCHAR)VIDEO_MEMORY_VIRTUAL_BASE) +#define VIDEO_CONTROL ((PG300_VIDEO_REGISTERS)VIDEO_CONTROL_VIRTUAL_BASE) +#define CURSOR_CONTROL ((PCURSOR_REGISTERS)VIDEO_CURSOR_VIRTUAL_BASE) + + +// +// Static data. +// + +BOOLEAN ControlSequence; +BOOLEAN EscapeSequence; +BOOLEAN FontSelection; +ULONG PCount; +LONG FwColumn; +LONG FwRow; +BOOLEAN FwHighIntensity; +BOOLEAN FwUnderscored; +BOOLEAN FwReverseVideo; +ULONG FwForegroundColor; +ULONG FwBackgroundColor; +PCHAR DisplayDevicePath = "multi(0)video(0)monitor(0)"; +ULONG DisplayWidth; +ULONG DisplayText; +ULONG FrameSize; +ULONG ScrollLine; +ULONG ScrollLength; +ULONG MaxRow; +ULONG MaxColumn; +ULONG CharacterHeight; +ULONG CharacterWidth; +ULONG CharacterSize; +PCHAR FwFont; +PCHAR FwLineDrawFont; +ULONG FontIncrement; +ULONG ColorTable[16] = { 0x00000000, + 0x0000000f, + 0x00000f00, + 0x00000f0f, + 0x000f0000, + 0x000f000f, + 0x000f0f00, + 0x000f0f0f, + 0x0f000000, + 0x0f00000f, + 0x0f000f00, + 0x0f000f0f, + 0x0f0f0000, + 0x0f0f000f, + 0x0f0f0f00, + 0x0f0f0f0f }; + +#define CONTROL_SEQUENCE_MAX_PARAMETER 10 + +ULONG Parameter[CONTROL_SEQUENCE_MAX_PARAMETER]; + +// +// Declare and initialize the default DefaultMonitor. +// +MONITOR_CONFIGURATION_DATA DefaultMonitor = { + 0, // version : do not change + 0, // revision : do not change + 1280, // HorizontalResolution + 11832, // HorizontalDisplayTime + 1596, // HorizontalBackPorch + 587, // HorizontalFrontPorch + 1745, // HorizontalSync + 1024, // VerticalResolution + 28, // VerticalBackPorch + 1, // VerticalFrontPorch + 3, // VerticalSync + 0, // HorizontalScreenSize : do not change + 0 // VerticalScreenSize : do not change +}; + +UCHAR LdHorizontalMask[12][4] = { 0xf8, 0x0f, 0x08, 0x08, + 0xcc, 0xcc, 0x08, 0x1c, + 0x5b, 0x5b, 0x08, 0x1c, + 0xaa, 0xaa, 0x08, 0x1c, + 0xf8, 0x0f, 0x1c, 0x1c, + 0x10, 0x04, 0x1c, 0x1c, + 0x10, 0x04, 0x08, 0x08, + 0xf0, 0x07, 0x08, 0x08, + 0xf0, 0x07, 0x14, 0x14, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xaa, 0x55 }; + +USHORT LdVerticalMask[12][4] = { 0x0080, 0x0080, 0x00ff, 0xff80, + 0x0080, 0x01c0, 0x0f0f, 0x0f0f, + 0x0080, 0x01c0, 0xe31c, 0xe31c, + 0x0080, 0x01c0, 0x3333, 0x3333, + 0x01c0, 0x01c0, 0x00ff, 0xff80, + 0x01c0, 0x01c0, 0x0040, 0x0100, + 0x0080, 0x0080, 0x0040, 0x0100, + 0x0080, 0x0080, 0x007f, 0xff00, + 0x0140, 0x0140, 0x007f, 0xff00, + 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0xaaaa, 0x5555 }; + + +UCHAR LdAsciiToUnicode[40] = {0x02, 0x24, 0x61, 0x62, 0x56, 0x55, 0x63, 0x51, + 0x57, 0x5d, 0x5c, 0x5b, 0x10, 0x14, 0x34, 0x2c, + 0x1c, 0x00, 0x3c, 0x5e, 0x5f, 0x5a, 0x54, 0x69, + 0x66, 0x60, 0x50, 0x6c, 0x67, 0x68, 0x64, 0x65, + 0x59, 0x58, 0x52, 0x53, 0x6b, 0x6a, 0x18, 0x0c }; + +// +// Declare externally defined data. +// + +extern UCHAR FwUsFont2[1]; +extern UCHAR FwRleFont[1]; +extern UCHAR FwLdFont[1]; + + +// +// Define routine prototypes. +// + +VOID +FwDisplayCharacter( + IN UCHAR Character, + IN BOOLEAN LineDrawCharacter + ); + + + +ULONG +FwPrint ( + PCHAR Format, + ... + ) + +{ + + va_list arglist; + UCHAR Buffer[256]; + ULONG Count; + ULONG Length; + + // + // Format the output into a buffer and then print it. + // + + va_start(arglist, Format); + Length = vsprintf(Buffer, Format, arglist); + DisplayWrite(Buffer, Length, &Count); + va_end(arglist); + return 0; +} + + + + +VOID +FwUnpackBitmap( + IN UCHAR HorizontalMask, + IN USHORT VerticalMask, + OUT PUCHAR Bitmap + ) + +/*++ + +Routine Description: + + This routine unpacks a bitmap that has been stored using the two-byte + per glyph encoding scheme. It is typically called four times per + glyph, for top, bottom, left, and right. + +Arguments: + + HorizontalMask - Supplies a horizontal mask value. + + Vertical Mask - Supplies a vertical mask value. + + Bitmap - Supplies a pointer to the output bitmap. + +Return Value: + + None. + +--*/ + +{ + PUCHAR TempChar; + PUSHORT TempShort; + ULONG I, J; + + // + // Check for 16x32 font. + // + + if (CharacterWidth == 16) { + + TempShort = (PUSHORT)Bitmap; + + // + // Loop 16 times, drawing two lines per loop. + // + + for ( I = 1 ; I <= 0x8000; I <<= 1 ) { + + // + // If the vertical mask is set, draw the horizontal mask. + // + + if (I & VerticalMask) { + + // + // Stretch the horizontal mask out by a factor of two. + // + + for (J = 0 ; J < 8 ; J++ ) { + if ((1 << J) & HorizontalMask) { + *TempShort |= 3 << (J * 2); + } + } + } + + // + // Duplicate the line and increment the pointer. + // + + *(TempShort + 1) = *TempShort; + TempShort += 2; + } + + // + // Assume 16x8 font. + // + + } else { + + TempChar = Bitmap; + + // + // Loop 16 times. + // + + for ( I = 1 ; I <= 0x8000; I <<= 1 ) { + + // + // If the vertical mask is set, draw the horizontal mask. + // + + if (I & VerticalMask) { + *TempChar |= HorizontalMask; + } + TempChar++; + } + } + + return; + +} + + +ARC_STATUS +DisplayWrite ( + IN PVOID Buffer, + IN ULONG Length, + OUT PULONG Count + ) + +/*++ + +Routine Description: + + This module implements the ARC firmware Console Output functions as + described in the Advanced Risc Computing Specification (Revision 1.00), + section 3.3.1.5.1 Basic Character Console, and section 3.3.1.5.2 Enhanced + Character Console for a MIPS R3000 or R4000 Jazz system. + + +Arguments: + + Buffer - Supplies a pointer to a buffer containing the characters to + be displayed. + + Length - Supplies the length of Buffer. + + Count - Returns the count of the characters that were displayed. + +Return Value: + + If the characters were successfully displayed, ESUCCESS is returned, + otherwise one of the following error codes is returned. + + EBADF The file descriptor specified by FileId is invalid. + + EIO An output error occurred. + +--*/ + +{ + PCONSOLE_CONTEXT Context; + ARC_STATUS Status; + PUCHAR String; + ULONG ColumnEndPoint; + ULONG RowEndPoint; + ULONG Index, Index2; + ULONG FGColor; + ULONG BGColor; + BOOLEAN Unicode; + + Unicode = FALSE; + + // + // Process each character in turn. + // + + Status = ESUCCESS; + String = (PUCHAR)Buffer; + + for ( *Count = 0 ; + *Count < Length ; + (*Count)++, String++ ) { + + // + // Check for Unicode character. + // + + if (Unicode) { + if (*Count & 1) { + + // + // Skip the upper half of each character. + // + + continue; + } else { + if (*(String + 1) == 0x25) { + + // + // If a Unicode line drawing character, go ahead and display + // it. + // + + if (*String <= 0x7f) { + FwDisplayCharacter(*String, TRUE); + } else { + FwDisplayCharacter(128, TRUE); + } + + if (FwColumn < MaxColumn) { + FwColumn++; + } + continue; + } else { + if (*(String + 1) != 0) { + + // + // Display an invalid character. + // + + FwDisplayCharacter(128, TRUE); + + if (FwColumn < MaxColumn) { + FwColumn++; + } + continue; + } + } + } + } + + // + // If we're in the middle of a control sequence, continue scanning, + // otherwise process character. + // + + if (ControlSequence) { + + // + // If the character is a digit, update parameter value. + // + + if ((*String >= '0') && (*String <= '9')) { + Parameter[PCount] = Parameter[PCount] * 10 + *String - '0'; + continue; + } + + // + // If we are in the middle of a font selection sequence, this + // character must be a 'D', otherwise reset control sequence. + // + + if (FontSelection) { + + //if (*String == 'D') { + // + // // + // // Other fonts not implemented yet. + // // + // + //} else { + //} + + ControlSequence = FALSE; + FontSelection = FALSE; + continue; + } + + switch (*String) { + + // + // If a semicolon, move to the next parameter. + // + + case ';': + + PCount++; + if (PCount > CONTROL_SEQUENCE_MAX_PARAMETER) { + PCount = CONTROL_SEQUENCE_MAX_PARAMETER; + } + Parameter[PCount] = 0; + break; + + // + // If a 'J', erase part or all of the screen. + // + + case 'J': + + switch (Parameter[0]) { + + // + // Erase to end of the screen. + // + + case 0: + // + // Clear to end of line by Writing char ' ' + // + ColumnEndPoint = FwColumn; + while (FwColumn <= MaxColumn) { + FwDisplayCharacter(' ', FALSE); + FwColumn++; + } + FwColumn = ColumnEndPoint; + if (FwRow+1 < MaxRow) { + // + // Zero the rest of the screen + // + FillVideoMemory((PUCHAR)(VIDEO_MEMORY + ((FwRow+1) * ScrollLine)), + FrameSize - ((FwRow+1) * ScrollLine), + FwBackgroundColor + ); + } + break; + + // + // Erase from the beginning of the screen. + // + + case 1: + if (FwRow) { + FillVideoMemory((PUCHAR)(VIDEO_MEMORY), + (FwRow * ScrollLine), + FwBackgroundColor + ); + } + ColumnEndPoint=FwColumn; + for (FwColumn=0; FwColumn < ColumnEndPoint; FwColumn++) { + FwDisplayCharacter(' ', FALSE); + } + break; + + // + // Erase entire screen. + // + + default : + FillVideoMemory(VIDEO_MEMORY, + FrameSize, + FwBackgroundColor); + FwRow = 0; + FwColumn = 0; + break; + } + + ControlSequence = FALSE; + break; + + // + // If a 'K', erase part or all of the line. + // + + case 'K': + + switch (Parameter[0]) { + + // + // Erase to end of the line. + // + + case 0: + ColumnEndPoint = FwColumn; + FwColumn = MaxColumn + 1; + do { + FwColumn--; + FwDisplayCharacter(' ', FALSE); + } while (FwColumn != ColumnEndPoint); + break; + + // + // Erase from the beginning of the line. + // + + case 1: + ColumnEndPoint = FwColumn; + FwColumn = -1; + do { + FwColumn++; + FwDisplayCharacter(' ', FALSE); + } while (FwColumn != ColumnEndPoint); + break; + + // + // Erase entire line. + // + + default : + FwColumn = MaxColumn + 1; + do { + FwColumn--; + FwDisplayCharacter(' ', FALSE); + } while (FwColumn != 0); + break; + } + + ControlSequence = FALSE; + break; + + // + // If a 'H', move cursor to position. + // + + case 'H': + + // + // Shift parameters to be 1 based. + // + + if (Parameter[0] != 0) { + Parameter[0] -= 1; + } + if (Parameter[1] != 0) { + Parameter[1] -= 1; + } + + FwRow = Parameter[0]; + if (FwRow > MaxRow) { + FwRow = MaxRow; + } + FwColumn = Parameter[1]; + if (FwColumn > MaxColumn) { + FwColumn = MaxColumn; + } + + ControlSequence = FALSE; + break; + + // + // If a 'A', move cursor up. + // + + case 'A': + + // + // A parameter of zero still means a cursor shift position of 1. + // + + if (Parameter[0] == 0) { + Parameter[0] = 1; + } + + if (Parameter[0] > FwRow) { + FwRow = 0; + } else { + FwRow -= Parameter[0]; + } + ControlSequence = FALSE; + break; + + // + // If a 'B', move cursor down. + // + + case 'B': + + // + // A parameter of zero still means a cursor shift position of 1. + // + + if (Parameter[0] == 0) { + Parameter[0] = 1; + } + + if (Parameter[0] + FwRow > MaxRow) { + FwRow = MaxRow; + } else { + FwRow += Parameter[0]; + } + ControlSequence = FALSE; + break; + + // + // If a 'C', move cursor right. + // + + case 'C': + + // + // A parameter of zero still means a cursor shift position of 1. + // + + if (Parameter[0] == 0) { + Parameter[0] = 1; + } + + if (Parameter[0] + FwColumn > MaxColumn) { + FwColumn = MaxColumn; + } else { + FwColumn += Parameter[0]; + } + ControlSequence = FALSE; + break; + + // + // If a 'D', move cursor left. + // + + case 'D': + + // + // A parameter of zero still means a cursor shift position of 1. + // + + if (Parameter[0] == 0) { + Parameter[0] = 1; + } + + if (Parameter[0] > FwColumn) { + FwColumn = 0; + } else { + FwColumn -= Parameter[0]; + } + ControlSequence = FALSE; + break; + + // + // If a ' ', could be a FNT selection command. + // + + case ' ': + FontSelection = TRUE; + break; + + // + // If a 'm', Select Graphics Rendition command. + // + + case 'm': + + // + // Select action based on each parameter. + // + + for ( Index = 0 ; Index <= PCount ; Index++ ) { + switch (Parameter[Index]) { + + // + // Attributes off. + // + + case 0: + FwHighIntensity = FALSE; + FwUnderscored = FALSE; + FwReverseVideo = FALSE; + break; + + // + // High Intensity. + // + + case 1: + FwHighIntensity = TRUE; + break; + + // + // Underscored. + // + + case 4: + FwUnderscored = TRUE; + break; + + // + // Reverse Video. + // + + case 7: + FwReverseVideo = TRUE; + break; + + // + // Font selection, not implemented yet. + // + + case 10: + case 11: + case 12: + case 13: + case 14: + case 15: + case 16: + case 17: + case 18: + case 19: + break; + + // + // Foreground Color. + // + + case 30: + case 31: + case 32: + case 33: + case 34: + case 35: + case 36: + case 37: + FwForegroundColor = Parameter[Index] - 30; + break; + + // + // Background Color. + // + + case 40: + case 41: + case 42: + case 43: + case 44: + case 45: + case 46: + case 47: + FwBackgroundColor = Parameter[Index] - 40; + break; + + default: + break; + } + } + + // + // Recompute color table. + // + + if (FwReverseVideo) { + FGColor = FwBackgroundColor; + BGColor = FwForegroundColor + (FwHighIntensity ? 0x08 : 0 ); + } else { + FGColor = FwForegroundColor + (FwHighIntensity ? 0x08 : 0 ); + BGColor = FwBackgroundColor; + } + + for ( Index2 = 0 ; Index2 < 16 ; Index2++ ) { + ColorTable[Index2] = ((Index2 & 8) ? FGColor : BGColor ) << 24 | + ((Index2 & 4) ? FGColor : BGColor ) << 16 | + ((Index2 & 2) ? FGColor : BGColor ) << 8 | + ((Index2 & 1) ? FGColor : BGColor ) ; + } + + ControlSequence = FALSE; + break; + + default: + ControlSequence = FALSE; + break; + } + + // + // This is not a control sequence, check for escape sequence + // + + } else { + + // + // If escape sequence, check for control sequence, otherwise + // process single character. + // + + if (EscapeSequence) { + + // + // Check for '[', means control sequence, any other following + // character is ignored. + // + + if (*String == '[') { + + ControlSequence = TRUE; + + // + // Initialize first parameter. + // + + PCount = 0; + Parameter[0] = 0; + } + EscapeSequence = FALSE; + + // + // This is not a control or escape sequence, process single character. + // + + } else { + + // + // Check for line drawing character + // + + if ((*String >= '³') && (*String <= 'Ú')) { + FwDisplayCharacter(LdAsciiToUnicode[*String - '³'], TRUE); + + if (FwColumn < MaxColumn) { + FwColumn++; + } + + // + // Check for printing character. + // + + } else if (((*String >= ' ') && (*String <= '~'))) { + + FwDisplayCharacter(*String, FALSE); + + if (FwColumn < MaxColumn) { + FwColumn++; + } + + } else { + + // + // Check for special characters. + // + + switch (*String) { + + // + // Control sequence. + // + + case ASCII_CSI: + ControlSequence = TRUE; + + // + // Initialize first parameter. + // + + PCount = 0; + Parameter[0] = 0; + break; + + // + // Check for escape sequence. + // + + case ASCII_ESC: + EscapeSequence = TRUE; + break; + + // + // Vertical tab/Form feed Line feed. + // + + case ASCII_LF: + case ASCII_VT: + case ASCII_FF: + if (FwRow == MaxRow) { + FwScrollVideo(); + } else { + FwRow++; + } + + break; + + // + // Carriage return. + // + + case ASCII_CR: + FwColumn = 0; + break; + + // + // NUL, no action. + // + + case ASCII_NUL: + break; + + // + // Ring bell, not implemented yet. + // + + case ASCII_BEL: + break; + + // + // Backspace. + // + + case ASCII_BS: + if (FwColumn != 0) { + FwColumn--; + } + break; + + // + // Horizontal tab. + // + + case ASCII_HT: + FwColumn = ((FwColumn / 8) + 1) * 8; + if (FwColumn > MaxColumn) { + FwColumn = MaxColumn; + } + break; + default: + break; + } + } + } + } + } + return Status; +} + +ARC_STATUS +DisplayBootInitialize ( + VOID + ) + +/*++ + +Routine Description: + + This routine initializes the video control registers, and clears the + video screen. + +Arguments: + + None. + +Return Value: + + If the video was initialized, ESUCCESS is returned, otherwise an error + code is returned. + +--*/ + +{ + ARC_STATUS Status; + ULONG Character; + ULONG I,J,K; + ULONG Side; + PUCHAR LdFont; + PUSHORT FontWord; + ULONG LdFontSize; + ULONG FontIndex; + BOOLEAN Bitmap[32*16]; + ULONG BitmapIndex; + ULONG Short, Byte, Bit; + JAZZ_VIDEO_TYPE VideoType; + + // + // Try to initialize G300. + // + + Status = ESUCCESS; + if (InitializeG300(&DefaultMonitor) != ESUCCESS) { + + // + // G300 did not initialize properly, try a video PROM. + // + + if (InitializeVideoFromProm(&DefaultMonitor) != ESUCCESS) { + + // + // There is no valid video PROM, try for a G364 without a video + // PROM. + // + + if (InitializeG364(&DefaultMonitor) != ESUCCESS) { + // + // No valid video initialization was found. + // + + Status = EINVAL; + } + } + } + + // + // Initialize static data. + // + + ControlSequence = FALSE; + EscapeSequence = FALSE; + FontSelection = FALSE; + FwColumn = 0; + FwRow = 0; + FwHighIntensity = FALSE; + FwUnderscored = FALSE; + FwReverseVideo = FALSE; + + // + // Initialize static data. + // + + FwForegroundColor = FW_COLOR_HI_WHITE; + FwBackgroundColor = FW_COLOR_BLUE; + DisplayWidth = DefaultMonitor.HorizontalResolution; + FrameSize = (DisplayWidth * DefaultMonitor.VerticalResolution); + + if (DisplayWidth >= 1280) { + CharacterHeight = 32; + CharacterWidth = 16; + CharacterSize = (CharacterHeight * (CharacterWidth / 8)); + } else { + CharacterHeight = 16; + CharacterWidth = 8; + CharacterSize = (CharacterHeight * (CharacterWidth / 8)); + } + + ScrollLine = (DisplayWidth * CharacterHeight); + ScrollLength = (ScrollLine * ((DefaultMonitor.VerticalResolution / CharacterHeight) - 1)); + MaxRow = ((DefaultMonitor.VerticalResolution / CharacterHeight) - 1); + MaxColumn = ((DisplayWidth / CharacterWidth) - 1); + FontIncrement = (DisplayWidth - CharacterWidth) / sizeof(ULONG); + + // + // Clear the line drawing font area. + // + + LdFontSize = (CharacterSize * 130); + RtlZeroMemory(FW_FONT_ADDRESS, LdFontSize); + + // + // Unpack the line drawing font. + // + + FwLineDrawFont = (PUCHAR)FW_FONT_ADDRESS; + LdFont = FwLdFont; + + for (Character = 0; Character < 129 ; Character++ ) { + Side = *LdFont >> 4; + if (Side != 0) { + Side -= 4; + FwUnpackBitmap(LdHorizontalMask[Side][0], LdVerticalMask[Side][0],FwLineDrawFont); + } + Side = *LdFont++ & 0xf; + if (Side != 0) { + Side -= 4; + FwUnpackBitmap(LdHorizontalMask[Side][1], LdVerticalMask[Side][1],FwLineDrawFont); + } + Side = *LdFont >> 4; + if (Side != 0) { + Side -= 4; + FwUnpackBitmap(LdHorizontalMask[Side][2], LdVerticalMask[Side][2],FwLineDrawFont); + } + Side = *LdFont++ & 0xf; + if (Side != 0) { + Side -= 4; + FwUnpackBitmap(LdHorizontalMask[Side][3], LdVerticalMask[Side][3],FwLineDrawFont); + } + FwLineDrawFont += CharacterHeight * (CharacterWidth / 8); + } + + // + // Reinitialize the pointer. + // + + FwLineDrawFont = (PUCHAR)FW_FONT_ADDRESS; + + // + // Unpack the ascii font. + // + + if (CharacterWidth == 16) { + + // + // Clear the first character (the space). + // + + FwFont = (PUCHAR)FW_FONT_ADDRESS + LdFontSize; + for (I = 0; I < CharacterSize ; I++ ) { + FwFont[I] = 0; + } + + // + // Unpack the RLE font. + // + + FontIndex = 0; + for ( Character = 1; Character < 95 ; Character++ ) { + + BitmapIndex = 0; + + // + // White space at the beginning of the character. + // + + for (I = FwRleFont[FontIndex++]; I ; I-- ) { + Bitmap[BitmapIndex++] = FALSE; + } + + // + // Unpack set/clear pairs until the end. + // + + while (Byte = FwRleFont[FontIndex++]) { + + // + // Set bits. + // + + for (I = Byte >> 4; I ; I-- ) { + Bitmap[BitmapIndex++] = TRUE; + } + + // + // Clear bits. + // + + for (I = Byte & 0xF; I ; I-- ) { + Bitmap[BitmapIndex++] = FALSE; + } + + } + + // + // Clear until the end. + // + + while (BitmapIndex < CharacterHeight*CharacterWidth) { + Bitmap[BitmapIndex++] = FALSE; + } + + // + // Pack the font. + // + + FontWord = (PUSHORT)(FwFont + (Character * CharacterSize)); + for (Short = 0; Short < CharacterHeight ; Short++ ) { + FontWord[Short] = 0; + for (Bit = 0; Bit < CharacterWidth ; Bit++) { + if (Bitmap[(Short*CharacterWidth) + Bit] ) { + FontWord[Short] |= 0x8000 >> Bit; + } + } + } + } + } else { + + // + // The small font is not packed. + // + + FwFont = FwUsFont2; + } + + // + // Set the video memory to the background color. + // + + FillVideoMemory(VIDEO_MEMORY,FrameSize,FwBackgroundColor); + + + return Status; +} + + + +ARC_STATUS +InitializeG300 ( + IN OUT PMONITOR_CONFIGURATION_DATA GlobalMonitor + ) + +/*++ + +Routine Description: + + This routine initializes the G300 video control registers, and clears the + video screen. + +Arguments: + + None. + +Return Value: + + If the video was initialized, ESUCCESS is returned, otherwise an error + code is returned. + +--*/ + +{ + ULONG ScreenUnitRate; + ULONG MultiplierValue; + ULONG HalfLineTime; + ULONG FrontPorch; + ULONG ShortDisplay; + ULONG BackPorch; + ULONG HalfSync; + ULONG TransferDelay; + ULONG DmaDisplay; + ULONG DataLong; + ULONG Index; + ULONG i; + PG300_VIDEO_REGISTERS VideoControl = VIDEO_CONTROL; + PCURSOR_REGISTERS CursorControl = CURSOR_CONTROL; + PMONITOR_CONFIGURATION_DATA CurrentMonitor; + BOOLEAN UpdateMonitor; + + CurrentMonitor = GlobalMonitor; + UpdateMonitor = FALSE; + + // + // Check to see if the Monitor parameters are valid. + // + + do { + + // + // Determine the desired screen unit rate, in picoseconds (a screen unit is + // four pixels). + // + + if ((CurrentMonitor->HorizontalDisplayTime != 0) && (CurrentMonitor->HorizontalResolution != 0)) { + ScreenUnitRate = (CurrentMonitor->HorizontalDisplayTime * 1000) * 4 / CurrentMonitor->HorizontalResolution; + } else { + continue; + } + + if (ScreenUnitRate == 0) { + continue; + } + + // + // Multiplier value is the oscillator period (in picoseconds) divided by + // the pixel rate. + // + + MultiplierValue = 123077 / (ScreenUnitRate / 4); + + if (MultiplierValue < 5 || MultiplierValue > 18) { + continue; + } + + break; + + // + // If the while is executed, the parameters are not valid. Set UpdateMonitor + // and point to the default parameters, which are valid. Note that the + // "while" will evaluate TRUE because the value of (a,b) is the value of b. + // + + } while (CurrentMonitor = &DefaultMonitor, UpdateMonitor = TRUE); + + // + // Initialize the G300B boot register value. + // + + DataLong = 0; + ((PG300_VIDEO_BOOT)(&DataLong))->Multiplier = MultiplierValue; + ((PG300_VIDEO_BOOT)(&DataLong))->ClockSelect = 1; + WRITE_REGISTER_ULONG(&VideoControl->Boot.Long, DataLong); + + // + // Wait a few cycles until the pll stabilizes. + // + + FwStallExecution(200); + + // + // Disable the G300B display controller. + // + + DataLong = 0; + ((PG300_VIDEO_PARAMETERS)(&DataLong))->PlainWave = 1; + WRITE_REGISTER_ULONG(&VideoControl->Parameters.Long, DataLong); + + // + // Determine if this is actually the G300 board. + // + + WRITE_REGISTER_UCHAR((PUCHAR)0xe0200000,0); + if (READ_REGISTER_UCHAR((PUCHAR)0xe0200000) != JazzVideoG300) { + return ENODEV; + } + + // + // Update the monitor parameters if necessary. + // + + if (UpdateMonitor) { + GlobalMonitor->HorizontalResolution = DefaultMonitor.HorizontalResolution; + GlobalMonitor->HorizontalDisplayTime = DefaultMonitor.HorizontalDisplayTime; + GlobalMonitor->HorizontalBackPorch = DefaultMonitor.HorizontalBackPorch; + GlobalMonitor->HorizontalFrontPorch = DefaultMonitor.HorizontalFrontPorch; + GlobalMonitor->HorizontalSync = DefaultMonitor.HorizontalSync; + GlobalMonitor->VerticalResolution = DefaultMonitor.VerticalResolution; + GlobalMonitor->VerticalBackPorch = DefaultMonitor.VerticalBackPorch; + GlobalMonitor->VerticalFrontPorch = DefaultMonitor.VerticalFrontPorch; + GlobalMonitor->VerticalSync = DefaultMonitor.VerticalSync; + } + + // + // Initialize the G300B operational values. + // + + HalfSync = (CurrentMonitor->HorizontalSync * 1000) / ScreenUnitRate / 2; + WRITE_REGISTER_ULONG(&VideoControl->HorizonalSync.Long, HalfSync ); + + BackPorch = (CurrentMonitor->HorizontalBackPorch * 1000) / ScreenUnitRate; + WRITE_REGISTER_ULONG(&VideoControl->BackPorch.Long, BackPorch ); + + WRITE_REGISTER_ULONG(&VideoControl->Display.Long, CurrentMonitor->HorizontalResolution / 4); + + // + // The LineTime needs to be an even number of units, so calculate LineTime / 2 + // and then multiply by two to program. ShortDisplay and BroadPulse also + // use LineTime / 2. + // + + HalfLineTime = (CurrentMonitor->HorizontalSync + CurrentMonitor->HorizontalFrontPorch + + CurrentMonitor->HorizontalBackPorch + CurrentMonitor->HorizontalDisplayTime) * 1000 / + ScreenUnitRate / 2; + + WRITE_REGISTER_ULONG(&VideoControl->LineTime.Long, HalfLineTime * 2); + + FrontPorch = (CurrentMonitor->HorizontalFrontPorch * 1000) / ScreenUnitRate; + ShortDisplay = HalfLineTime - ((HalfSync * 2) + BackPorch + FrontPorch); + WRITE_REGISTER_ULONG(&VideoControl->ShortDisplay.Long, ShortDisplay); + + WRITE_REGISTER_ULONG(&VideoControl->BroadPulse.Long, HalfLineTime - FrontPorch); + + WRITE_REGISTER_ULONG(&VideoControl->VerticalSync.Long, CurrentMonitor->VerticalSync * 2); + + WRITE_REGISTER_ULONG(&VideoControl->VerticalBlank.Long, + (CurrentMonitor->VerticalFrontPorch + CurrentMonitor->VerticalBackPorch - + (CurrentMonitor->VerticalSync * 2)) * 2); + + WRITE_REGISTER_ULONG(&VideoControl->VerticalDisplay.Long, CurrentMonitor->VerticalResolution * 2); + + WRITE_REGISTER_ULONG(&VideoControl->LineStart.Long, LINE_START_VALUE); + + // + // TransferDelay must be less than BackPorch and ShortDisplay. Note: When + // 50 MHz chips are everywhere, TransferDelay should have a maximum value + // to minimize the graphics overhead. + // + + if (BackPorch < ShortDisplay) { + TransferDelay = BackPorch - 1; + } else { + TransferDelay = ShortDisplay - 1; + } + + WRITE_REGISTER_ULONG(&VideoControl->TransferDelay.Long, TransferDelay); + + // + // DMA display (also known as MemInit) is 1024 (the length of the VRAM + // shift register) minus TransferDelay. + // + + DmaDisplay = 1024 - TransferDelay; + WRITE_REGISTER_ULONG(&VideoControl->DmaDisplay.Long, DmaDisplay); + + WRITE_REGISTER_ULONG(&VideoControl->PixelMask.Long, G300_PIXEL_MASK_VALUE); + + // + // Set up the color map. + // + + WRITE_REGISTER_ULONG(&VideoControl->ColorMapData[FW_COLOR_BLACK], + G300_PALETTE_BLACK); + WRITE_REGISTER_ULONG(&VideoControl->ColorMapData[FW_COLOR_RED], + G300_PALETTE_RED); + WRITE_REGISTER_ULONG(&VideoControl->ColorMapData[FW_COLOR_GREEN], + G300_PALETTE_GREEN); + WRITE_REGISTER_ULONG(&VideoControl->ColorMapData[FW_COLOR_YELLOW], + G300_PALETTE_YELLOW); + WRITE_REGISTER_ULONG(&VideoControl->ColorMapData[FW_COLOR_BLUE], + G300_PALETTE_BLUE); + WRITE_REGISTER_ULONG(&VideoControl->ColorMapData[FW_COLOR_MAGENTA], + G300_PALETTE_MAGENTA); + WRITE_REGISTER_ULONG(&VideoControl->ColorMapData[FW_COLOR_CYAN], + G300_PALETTE_CYAN); + WRITE_REGISTER_ULONG(&VideoControl->ColorMapData[FW_COLOR_WHITE], + G300_PALETTE_WHITE); + WRITE_REGISTER_ULONG(&VideoControl->ColorMapData[FW_COLOR_HI_BLACK], + G300_PALETTE_HI_BLACK); + WRITE_REGISTER_ULONG(&VideoControl->ColorMapData[FW_COLOR_HI_RED], + G300_PALETTE_HI_RED); + WRITE_REGISTER_ULONG(&VideoControl->ColorMapData[FW_COLOR_HI_GREEN], + G300_PALETTE_HI_GREEN); + WRITE_REGISTER_ULONG(&VideoControl->ColorMapData[FW_COLOR_HI_YELLOW], + G300_PALETTE_HI_YELLOW); + WRITE_REGISTER_ULONG(&VideoControl->ColorMapData[FW_COLOR_HI_BLUE], + G300_PALETTE_HI_BLUE); + WRITE_REGISTER_ULONG(&VideoControl->ColorMapData[FW_COLOR_HI_MAGENTA], + G300_PALETTE_HI_MAGENTA); + WRITE_REGISTER_ULONG(&VideoControl->ColorMapData[FW_COLOR_HI_CYAN], + G300_PALETTE_HI_CYAN); + WRITE_REGISTER_ULONG(&VideoControl->ColorMapData[FW_COLOR_HI_WHITE], + G300_PALETTE_HI_WHITE); + + // + // Initialize the G300B control parameters. + // + + DataLong = 0; + ((PG300_VIDEO_PARAMETERS)(&DataLong))->EnableVideo = 1; + ((PG300_VIDEO_PARAMETERS)(&DataLong))->PlainWave = 1; + ((PG300_VIDEO_PARAMETERS)(&DataLong))->SeparateSync = 1; + ((PG300_VIDEO_PARAMETERS)(&DataLong))->DelaySync = G300_DELAY_SYNC_CYCLES; + ((PG300_VIDEO_PARAMETERS)(&DataLong))->BlankOutput = 1; + ((PG300_VIDEO_PARAMETERS)(&DataLong))->BitsPerPixel = EIGHT_BITS_PER_PIXEL; + ((PG300_VIDEO_PARAMETERS)(&DataLong))->AddressStep = 2; + WRITE_REGISTER_ULONG(&VideoControl->Parameters.Long, DataLong); + + // + // Disable the cursor parts. + // + + WRITE_REGISTER_USHORT(&CursorControl->AddressPointer0.Short,0); + WRITE_REGISTER_USHORT(&CursorControl->AddressPointer1.Short,0); + + // + // Clear cursor control. + // + + for (i=0;i<13;i++) { + WRITE_REGISTER_USHORT(&CursorControl->CursorControl.Short,0); + } + + // + // Clear Cursor Memory + // + + for (i=0;i<512;i++) { + WRITE_REGISTER_USHORT(&CursorControl->CursorMemory.Short,0); + } + + return ESUCCESS; +} + +ARC_STATUS +InitializeG364 ( + IN OUT PMONITOR_CONFIGURATION_DATA GlobalMonitor + ) + +/*++ + +Routine Description: + + This routine initializes the G364 video control registers, and clears the + video screen. + +Arguments: + + None. + +Return Value: + + If the video was initialized, ESUCCESS is returned, otherwise an error + code is returned. + +--*/ + +{ + ULONG ScreenUnitRate; + ULONG MultiplierValue; + ULONG HalfLineTime; + ULONG FrontPorch; + ULONG BackPorch; + ULONG HalfSync; + ULONG TransferDelay; + ULONG DmaDisplay; + ULONG DataLong; + ULONG Index; + PG364_VIDEO_REGISTERS VideoControl = (PG364_VIDEO_REGISTERS) (VIDEO_CONTROL_VIRTUAL_BASE + 0x80000); + PMONITOR_CONFIGURATION_DATA CurrentMonitor; + BOOLEAN UpdateMonitor; + JAZZ_VIDEO_TYPE FwVideoType; + + // + // Determine if this is actually the G364 board. + // + + if (READ_REGISTER_UCHAR((PUCHAR)(VIDEO_CONTROL_VIRTUAL_BASE)) == JazzVideoG364) { + FwVideoType = JazzVideoG364; + } else { + FwVideoType = MipsVideoG364; + } + + // + // Reset the whole video board. + // + + WRITE_REGISTER_UCHAR((PUCHAR)(VIDEO_CONTROL_VIRTUAL_BASE+0x180000),0); + + CurrentMonitor = GlobalMonitor; + UpdateMonitor = FALSE; + + // + // Check to see if the Monitor parameters are valid. + // + + do { + + // + // Determine the desired screen unit rate, in picoseconds (a screen unit is + // four pixels). + // + + if ((CurrentMonitor->HorizontalDisplayTime != 0) && (CurrentMonitor->HorizontalResolution != 0)) { + ScreenUnitRate = (CurrentMonitor->HorizontalDisplayTime * 1000) * 4 / CurrentMonitor->HorizontalResolution; + } else { + continue; + } + + if (ScreenUnitRate == 0) { + continue; + } + + // + // Multiplier value is the oscillator period (in picoseconds) divided by + // the pixel rate. + // + + if (FwVideoType == JazzVideoG364) { + MultiplierValue = 123077 / (ScreenUnitRate / 4); + if (MultiplierValue < 5 || MultiplierValue > 18) { + continue; + } + } else { + MultiplierValue = 200000 / (ScreenUnitRate / 4); + if (MultiplierValue < 5 || MultiplierValue > 29) { + continue; + } + } + + + break; + + // + // If the while is executed, the parameters are not valid. Set UpdateMonitor + // and point to the default parameters, which are valid. Note that the + // "while" will evaluate TRUE because the value of (a,b) is the value of b. + // + + } while (CurrentMonitor = &DefaultMonitor, UpdateMonitor = TRUE); + + // + // Update the monitor parameters if necessary. + // + + if (UpdateMonitor) { + GlobalMonitor->HorizontalResolution = DefaultMonitor.HorizontalResolution; + GlobalMonitor->HorizontalDisplayTime = DefaultMonitor.HorizontalDisplayTime; + GlobalMonitor->HorizontalBackPorch = DefaultMonitor.HorizontalBackPorch; + GlobalMonitor->HorizontalFrontPorch = DefaultMonitor.HorizontalFrontPorch; + GlobalMonitor->HorizontalSync = DefaultMonitor.HorizontalSync; + GlobalMonitor->VerticalResolution = DefaultMonitor.VerticalResolution; + GlobalMonitor->VerticalBackPorch = DefaultMonitor.VerticalBackPorch; + GlobalMonitor->VerticalFrontPorch = DefaultMonitor.VerticalFrontPorch; + GlobalMonitor->VerticalSync = DefaultMonitor.VerticalSync; + } + + // + // write multiplier value + // + + DataLong = 0; + ((PG364_VIDEO_BOOT)(&DataLong))->ClockSelect = 1; + ((PG364_VIDEO_BOOT)(&DataLong))->MicroPort64Bits = 1; + ((PG364_VIDEO_BOOT)(&DataLong))->Multiplier = MultiplierValue; + WRITE_REGISTER_ULONG(&VideoControl->Boot.Long, DataLong); + + // + // Initialize the G364 control parameters. + // + + DataLong = 0; + + // + // If vertical front porch is 1, use tesselated sync, otherwise use normal sync. + // + + if (CurrentMonitor->VerticalFrontPorch > 1) { + ((PG364_VIDEO_PARAMETERS)(&DataLong))->PlainSync = 1; + } + ((PG364_VIDEO_PARAMETERS)(&DataLong))->DelaySync = G364_DELAY_SYNC_CYCLES; + ((PG364_VIDEO_PARAMETERS)(&DataLong))->BitsPerPixel = EIGHT_BITS_PER_PIXEL; + ((PG364_VIDEO_PARAMETERS)(&DataLong))->AddressStep = G364_ADDRESS_STEP_INCREMENT; + ((PG364_VIDEO_PARAMETERS)(&DataLong))->DisableCursor = 1; + WRITE_REGISTER_ULONG(&VideoControl->Parameters.Long, DataLong); + + // + // Initialize the G364 operational values. + // + + HalfSync = (CurrentMonitor->HorizontalSync * 1000) / ScreenUnitRate / 2; + WRITE_REGISTER_ULONG(&VideoControl->HorizontalSync.Long, HalfSync ); + + BackPorch = (CurrentMonitor->HorizontalBackPorch * 1000) / ScreenUnitRate; + WRITE_REGISTER_ULONG(&VideoControl->BackPorch.Long, BackPorch ); + + WRITE_REGISTER_ULONG(&VideoControl->Display.Long, CurrentMonitor->HorizontalResolution / 4); + + // + // The LineTime needs to be an even number of units, so calculate LineTime / 2 + // and then multiply by two to program. ShortDisplay and BroadPulse also + // use LineTime / 2. + // + + HalfLineTime = (CurrentMonitor->HorizontalSync + CurrentMonitor->HorizontalFrontPorch + + CurrentMonitor->HorizontalBackPorch + CurrentMonitor->HorizontalDisplayTime) * 1000 / + ScreenUnitRate / 2; + + WRITE_REGISTER_ULONG(&VideoControl->LineTime.Long, HalfLineTime * 2); + + FrontPorch = (CurrentMonitor->HorizontalFrontPorch * 1000) / ScreenUnitRate; + WRITE_REGISTER_ULONG(&VideoControl->ShortDisplay.Long, + HalfLineTime - ((HalfSync * 2) + BackPorch + FrontPorch)); + + WRITE_REGISTER_ULONG(&VideoControl->BroadPulse.Long, HalfLineTime - FrontPorch); + + WRITE_REGISTER_ULONG(&VideoControl->VerticalSync.Long, CurrentMonitor->VerticalSync * 2); + WRITE_REGISTER_ULONG(&VideoControl->VerticalPreEqualize.Long, CurrentMonitor->VerticalFrontPorch * 2); + WRITE_REGISTER_ULONG(&VideoControl->VerticalPostEqualize.Long, 1 * 2); + + WRITE_REGISTER_ULONG(&VideoControl->VerticalBlank.Long, + (CurrentMonitor->VerticalBackPorch - 1) * 2); + + WRITE_REGISTER_ULONG(&VideoControl->VerticalDisplay.Long, CurrentMonitor->VerticalResolution * 2); + + WRITE_REGISTER_ULONG(&VideoControl->LineStart.Long, LINE_START_VALUE); + + // + // Transfer delay is 1.65 microseconds expressed in screen units, plus 1. + // + + TransferDelay = (1650000 / ScreenUnitRate) + 1; + + if (BackPorch <= TransferDelay) { + TransferDelay = BackPorch - 1; + } + WRITE_REGISTER_ULONG(&VideoControl->TransferDelay.Long, TransferDelay); + + // + // DMA display (also known as MemInit) is 1024 (the length of the VRAM + // shift register) minus TransferDelay. + // + + DmaDisplay = 1024 - TransferDelay; + WRITE_REGISTER_ULONG(&VideoControl->DmaDisplay.Long, DmaDisplay); + + WRITE_REGISTER_ULONG(&VideoControl->PixelMask.Long, G364_PIXEL_MASK_VALUE); + + // + // Set up the color map. + // + + WRITE_REGISTER_ULONG(&VideoControl->ColorMapData[FW_COLOR_BLACK], + G364_PALETTE_BLACK); + WRITE_REGISTER_ULONG(&VideoControl->ColorMapData[FW_COLOR_RED], + G364_PALETTE_RED); + WRITE_REGISTER_ULONG(&VideoControl->ColorMapData[FW_COLOR_GREEN], + G364_PALETTE_GREEN); + WRITE_REGISTER_ULONG(&VideoControl->ColorMapData[FW_COLOR_YELLOW], + G364_PALETTE_YELLOW); + WRITE_REGISTER_ULONG(&VideoControl->ColorMapData[FW_COLOR_BLUE], + G364_PALETTE_BLUE); + WRITE_REGISTER_ULONG(&VideoControl->ColorMapData[FW_COLOR_MAGENTA], + G364_PALETTE_MAGENTA); + WRITE_REGISTER_ULONG(&VideoControl->ColorMapData[FW_COLOR_CYAN], + G364_PALETTE_CYAN); + WRITE_REGISTER_ULONG(&VideoControl->ColorMapData[FW_COLOR_WHITE], + G364_PALETTE_WHITE); + WRITE_REGISTER_ULONG(&VideoControl->ColorMapData[FW_COLOR_HI_BLACK], + G364_PALETTE_HI_BLACK); + WRITE_REGISTER_ULONG(&VideoControl->ColorMapData[FW_COLOR_HI_RED], + G364_PALETTE_HI_RED); + WRITE_REGISTER_ULONG(&VideoControl->ColorMapData[FW_COLOR_HI_GREEN], + G364_PALETTE_HI_GREEN); + WRITE_REGISTER_ULONG(&VideoControl->ColorMapData[FW_COLOR_HI_YELLOW], + G364_PALETTE_HI_YELLOW); + WRITE_REGISTER_ULONG(&VideoControl->ColorMapData[FW_COLOR_HI_BLUE], + G364_PALETTE_HI_BLUE); + WRITE_REGISTER_ULONG(&VideoControl->ColorMapData[FW_COLOR_HI_MAGENTA], + G364_PALETTE_HI_MAGENTA); + WRITE_REGISTER_ULONG(&VideoControl->ColorMapData[FW_COLOR_HI_CYAN], + G364_PALETTE_HI_CYAN); + WRITE_REGISTER_ULONG(&VideoControl->ColorMapData[FW_COLOR_HI_WHITE], + G364_PALETTE_HI_WHITE); + + // + // Enable the G364 + // + + ((PG364_VIDEO_PARAMETERS)(&DataLong))->EnableVideo = 1; + WRITE_REGISTER_ULONG(&VideoControl->Parameters.Long, DataLong); + + // + // G364 C04 bug # 6: + // "The action of starting the VTG may cause the TopOfScreen register to become corrupted" + // + + WRITE_REGISTER_ULONG(&VideoControl->TopOfScreen, 0); + + return ESUCCESS; +} + + +VOID +FwDisplayCharacter ( + IN UCHAR Character, + IN BOOLEAN LineDrawCharacter + ) + +/*++ + +Routine Description: + + This routine displays a single character on the video screen at the current + cursor location with the current color and video attributes. It finds the + font bitmap and calls FwOutputCharacter to actually do the display. + +Arguments: + + Character - Supplies the character to be displayed. + + LineDrawCharacter - If true the current character is a line drawing character. + +Return Value: + + None. + +--*/ + +{ + PULONG Destination; + + Destination = (PULONG)(VIDEO_MEMORY + + (FwRow * ScrollLine) + (FwColumn * CharacterWidth)); + + if (!LineDrawCharacter) { + + FwOutputCharacter((PVOID)&FwFont[(Character - 0x20) * CharacterSize], + FwRow, + FwColumn); + } else { + + FwOutputCharacter((PVOID)&FwLineDrawFont[Character * CharacterSize], + FwRow, + FwColumn); + + } + return; +} + +VOID +FwOutputCharacter ( + IN PVOID Character, + IN ULONG Row, + IN ULONG Column + ) + +/*++ + +Routine Description: + + This routine displays a single character on the video screen at the current + cursor location with the current color and video attributes. It assumes + the character locations are word aligned. + +Arguments: + + Character - Supplies the character to be displayed. + +Return Value: + + None. + +--*/ + +{ + UCHAR DataByte; + USHORT DataShort; + PULONG Destination; + ULONG I, J; + + Destination = (PULONG)(VIDEO_MEMORY + + (Row * ScrollLine) + (Column * CharacterWidth)); + + if (CharacterWidth == 16) { + for (I = 0; I < CharacterHeight; I += 1) { + DataShort = *((PUSHORT)Character)++; + *Destination++ = ColorTable[DataShort & 0x0f]; + *Destination++ = ColorTable[(DataShort >> 4) & 0x0f]; + *Destination++ = ColorTable[(DataShort >> 8) & 0x0f]; + *Destination++ = ColorTable[DataShort >> 12]; + Destination += FontIncrement; + } + } else if (CharacterWidth == 12) { + for (I = 0; I < CharacterHeight; I += 2) { + + DataByte = *((PUCHAR)Character)++; + *Destination++ = ColorTable[DataByte & 0x0f]; + *Destination++ = ColorTable[(DataByte >> 4) & 0x0f]; + DataByte = *((PUCHAR)Character)++; + *Destination++ = ColorTable[DataByte & 0x0f]; + Destination += FontIncrement; + + *Destination++ = ColorTable[(DataByte >> 4) & 0x0f]; + DataByte = *((PUCHAR)Character)++; + *Destination++ = ColorTable[DataByte & 0x0f]; + *Destination++ = ColorTable[(DataByte >> 4) & 0x0f]; + Destination += FontIncrement; + } + } else { + for (I = 0; I < CharacterHeight; I += 1) { + DataByte = *((PUCHAR)Character)++; + *Destination++ = ColorTable[DataByte & 0x0f]; + *Destination++ = ColorTable[DataByte >> 4]; + Destination += FontIncrement; + } + } + + return; +} + +VOID +FwScrollVideo ( + VOID + ) + +/*++ + +Routine Description: + + This routine scrolls the display up one line. + +Arguments: + + None. + +Return Value: + + None. + +--*/ + +{ + ULONG SaveColumn; + + // + // Call the assembly language routine to do the actual scroll. + // + + FwVideoScroll((PVOID)(VIDEO_MEMORY + ScrollLine), + (PVOID)(VIDEO_MEMORY + ScrollLine + ScrollLength), + (PVOID)VIDEO_MEMORY); + + SaveColumn = FwColumn; + + // + // Set the bottom line to be the background color. + // + + for (FwColumn = MaxColumn ; + FwColumn >= 0 ; + FwColumn-- ) { + FwDisplayCharacter(' ', FALSE); + } + + FwColumn = SaveColumn; + return; +} + + +VOID +FwStallExecution ( + IN ULONG MicroSeconds + ) + +/*++ + +Routine Description: + + This function stalls execution for the specified number of microseconds. + +Arguments: + + MicroSeconds - Supplies the number of microseconds that execution is to be + stalled. + +Return Value: + + None. + +--*/ + +{ + + ULONG Index; + ULONG Limit; + PULONG Store; + ULONG Value; + + // + // ****** begin temporary code ****** + // + // This code must be replaced with a smarter version. For now it assumes + // an execution rate of 50,000,000 instructions per second and 4 instructions + // per iteration. + // + + Store = &Value; + Limit = (MicroSeconds * 50 / 4); + for (Index = 0; Index < Limit; Index += 1) { + *Store = Index; + } + return; +} + + +#endif diff --git a/private/ntos/fw/duobase/mips/jxfwhal.h b/private/ntos/fw/duobase/mips/jxfwhal.h new file mode 100644 index 000000000..91adf8168 --- /dev/null +++ b/private/ntos/fw/duobase/mips/jxfwhal.h @@ -0,0 +1,95 @@ +/*++ BUILD Version: 0001 // Increment this if a change has global effects + +Copyright (c) 1991 Microsoft Corporation + +Module Name: + + jxfwhal.h + +Abstract: + + This header file defines the private Hardware Architecture Layer (HAL) + Jazz specific interfaces, defines and structures. + +Author: + + Jeff Havens (jhavens) 09-Aug-91 + + +Revision History: + +--*/ + +#ifndef _JXFWHAL_ +#define _JXFWHAL_ + + +// +// Define global data used to locate the EISA control space and the realtime +// clock registers. +// + +extern PVOID HalpEisaControlBase; +extern PVOID HalpRealTimeClockBase; + +// +// Define adapter object structure. +// + +typedef struct _ADAPTER_OBJECT { + CSHORT Type; + CSHORT Size; + ULONG MapRegistersPerChannel; + PVOID AdapterBaseVa; + PVOID MapRegisterBase; + ULONG NumberOfMapRegisters; + BOOLEAN AdapterInUse; + UCHAR ChannelNumber; + UCHAR AdapterNumber; + UCHAR AdapterMode; + PUCHAR PagePort; +} ADAPTER_OBJECT; + +// +// Define function prototypes. +// + +PADAPTER_OBJECT +HalpAllocateEisaAdapter( + IN PDEVICE_DESCRIPTION DeviceDescription + ); + +BOOLEAN +HalpCreateEisaStructures( + VOID + ); + +VOID +HalpDisableEisaInterrupt( + IN CCHAR Vector + ); + +BOOLEAN +HalpEisaDispatch( + IN PKINTERRUPT Interrupt, + IN PVOID ServiceContext + ); + +VOID +HalpEisaMapTransfer( + IN PADAPTER_OBJECT AdapterObject, + IN ULONG Offset, + IN ULONG Length, + IN BOOLEAN WriteToDevice + ); + +VOID +HalpEnableEisaInterrupt( + IN CCHAR Vector, + IN KINTERRUPT_MODE InterruptMode + ); + + +// #define HalpAllocateEisaAdapter(DeviceDescritption) NULL + +#endif // _JXFWHAL_ diff --git a/private/ntos/fw/duobase/mips/jxhwsup.c b/private/ntos/fw/duobase/mips/jxhwsup.c new file mode 100644 index 000000000..f53323070 --- /dev/null +++ b/private/ntos/fw/duobase/mips/jxhwsup.c @@ -0,0 +1,927 @@ + +/*++ + +Copyright (c) 1990 Microsoft Corporation + +Module Name: + + jxhwsup.c + +Abstract: + + This module contains the IopXxx routines for the NT I/O system that + are hardware dependent. Were these routines not hardware dependent, + they would normally reside in the internal.c module. + +Author: + + Jeff Havens (jhavens) 14-Feb-1990 + +Environment: + + Kernel mode, local to I/O system + +Revision History: + + +--*/ +#include "fwp.h" +#include "jxfwhal.h" +#include "eisa.h" +#include "duobase.h" + + +PADAPTER_OBJECT +IopAllocateAdapter( + IN ULONG MapRegistersPerChannel, + IN PVOID AdapterBaseVa, + IN PVOID MapRegisterBase + ); + + +PADAPTER_OBJECT +HalGetAdapter( + IN PDEVICE_DESCRIPTION DeviceDescription, + IN OUT PULONG NumberOfMapRegisters + ) + +/*++ + +Routine Description: + + This function returns the appropriate adapter object for the device defined + in the device description structure. Three bus types are supported for the + Jazz system: Internal, Isa, and Eisa. + +Arguments: + + DeviceDescription - Supplies a description of the deivce. + + NumberOfMapRegisters - Returns the maximum number of map registers which + may be allocated by the device driver. + +Return Value: + + A pointer to the requested adpater object or NULL if an adapter could not + be created. + +--*/ + +{ + PADAPTER_OBJECT adapterObject; + UCHAR adapterMode; + + // + // Make sure this is the correct version. + // + + if (DeviceDescription->Version != DEVICE_DESCRIPTION_VERSION) { + + return(NULL); + + } + + // + // Set the maximum number of map registers if requested. + // + + if (NumberOfMapRegisters != NULL) { + + // + // Return half the total number of map registers per channel. + // + + *NumberOfMapRegisters = DMA_TRANSLATION_LIMIT / sizeof(TRANSLATION_ENTRY); + } + + if (DeviceDescription->InterfaceType == Internal) { + + // + // Return the adapter pointer for internal adapters. + // + // If this is a master controler such as the SONIC then return the + // last channel. + // + + if (DeviceDescription->Master) { + + adapterObject = IopAllocateAdapter(0,NULL,NULL); + adapterObject->PagePort = ~0; + adapterMode = 0; + ((PDMA_EISA_MODE) &adapterMode)->RequestMode = CASCADE_REQUEST_MODE; + adapterObject->AdapterMode = adapterMode; + return(adapterObject); + + + } else { + + // + // Internal channels not supported + // + + return(NULL); + } + + } + + // + // If the request is for a unsupported bus then return NULL. + // + + if ((DeviceDescription->InterfaceType != Isa) && + (DeviceDescription->InterfaceType != Eisa)) { + + // + // This bus type is unsupported return NULL. + // + + return(NULL); + } + + // + // Create an adapter object. + // + + adapterObject = HalpAllocateEisaAdapter( DeviceDescription ); + + return(adapterObject); +} + +BOOLEAN +HalTranslateBusAddress( + IN INTERFACE_TYPE InterfaceType, + IN ULONG BusNumber, + IN PHYSICAL_ADDRESS BusAddress, + IN OUT PULONG AddressSpace, + OUT PPHYSICAL_ADDRESS TranslatedAddress + ) + +/*++ + +Routine Description: + + This function returns the system physical address for a specified I/O bus + address. The return value is suitable for use in a subsequent call to + MmMapIoSpace. + +Arguments: + + InterfaceType - Supplies the type of bus which the address is for. + + BusNumber - Supplies the bus number for the device. + + BusAddress - Supplies the bus relative address. + + AddressSpace - Supplies the address space number for the device: 0 for + memory and 1 for I/O space. Returns the address space on this system. + +Return Value: + + Returns the system physical address for the specificed bus address. + +--*/ + +{ + TranslatedAddress->HighPart = 0; + TranslatedAddress->LowPart = 0; + + // + // If this is for the internal bus then just return the passed parameter. + // + + if (InterfaceType == Internal) { + + // + // Return the passed parameters. + // + + + TranslatedAddress->LowPart = BusAddress.LowPart; + return(TRUE); + } + + if (InterfaceType != Isa && InterfaceType != Eisa) { + + // + // Not on this system return nothing. + // + + *AddressSpace = 0; + return (FALSE); + } + + // + // Jazz only has one I/O bus which is an EISA, so the bus number is unused. + // + // Determine the address based on whether the bus address is in I/O space + // or bus memory space. + // + + if (*AddressSpace) { + + // + // The address is in I/O space. + // + + *AddressSpace = 0; + TranslatedAddress->LowPart = BusAddress.LowPart + EISA_CONTROL_PHYSICAL_BASE; + return(TRUE); + + } else { + + // + // The address is in memory space. + // + + *AddressSpace = 0; + TranslatedAddress->LowPart = BusAddress.LowPart + EISA_MEMORY_PHYSICAL_BASE; + return(TRUE); + } +} + +PADAPTER_OBJECT +IopAllocateAdapter( + IN ULONG MapRegistersPerChannel, + IN PVOID AdapterBaseVa, + IN PVOID MapRegisterBase + ) + +/*++ + +Routine Description: + + This routine allocates and initializes an adapter object to represent an + adapter or a DMA controller on the system. + +Arguments: + + MapRegistersPerChannel - Unused. + + AdapterBaseVa - Base virtual address of the adapter itself. If AdpaterBaseVa + is NULL then the MasterAdapterObject is allocated. + + MapRegisterBase - Unused. + +Return Value: + + The function value is a pointer to the allocate adapter object. + +--*/ + +{ + + PADAPTER_OBJECT AdapterObject; + KSPIN_LOCK SpinLock; + OBJECT_ATTRIBUTES ObjectAttributes; + ULONG Size; + ULONG BitmapSize; + HANDLE Handle; + NTSTATUS Status; + ULONG Mode; + + // + // Determine the size of the adapter. + // + + Size = sizeof( ADAPTER_OBJECT ); + + // + // Now create the adapter object. + // + + AdapterObject = FwAllocatePool(Size); + + // + // If the adapter object was successfully created, then attempt to insert + // it into the the object table. + // + + if (AdapterObject) { + + // + // Initialize the adapter object itself. + // + + AdapterObject->Type = IO_TYPE_ADAPTER; + AdapterObject->Size = Size; + AdapterObject->MapRegistersPerChannel = + DMA_TRANSLATION_LIMIT / sizeof( TRANSLATION_ENTRY); + AdapterObject->AdapterBaseVa = AdapterBaseVa; + AdapterObject->PagePort = NULL; + AdapterObject->AdapterInUse = FALSE; + + // + // Read the map register base from the Dma registers. + // + + AdapterObject->MapRegisterBase = (PVOID) (READ_REGISTER_ULONG( + &DMA_CONTROL->TranslationBase.Long) | KSEG1_BASE); + + + } else { + + // + // An error was incurred for some reason. Set the return value + // to NULL. + // + + return(NULL); + } + + return AdapterObject; + +} + +NTSTATUS +IoAllocateAdapterChannel( + IN PADAPTER_OBJECT AdapterObject, + IN PDEVICE_OBJECT DeviceObject, + IN ULONG NumberOfMapRegisters, + IN PDRIVER_CONTROL ExecutionRoutine, + IN PVOID Context + ) + +/*++ + +Routine Description: + + This routine allocates the adapter channel specified by the adapter object. + This is accomplished by placing the device object of the driver that wants + to allocate the adapter on the adapter's queue. If the queue is already + "busy", then the adapter has already been allocated, so the device object + is simply placed onto the queue and waits until the adapter becomes free. + + Once the adapter becomes free (or if it already is), then the driver's + execution routine is invoked. + + Also, a number of map registers may be allocated to the driver by specifying + a non-zero value for NumberOfMapRegisters. Then the map register must be + allocated from the master adapter. Once there are a sufficient number of + map registers available, then the execution routine is called and the + base address of the allocated map registers in the adapter is also passed + to the driver's execution routine. + +Arguments: + + AdapterObject - Pointer to the adapter control object to allocate to the + driver. + + DeviceObject - Pointer to the driver's device object that represents the + device allocating the adapter. + + NumberOfMapRegisters - The number of map registers that are to be allocated + from the channel, if any. + + ExecutionRoutine - The address of the driver's execution routine that is + invoked once the adapter channel (and possibly map registers) have been + allocated. + + Context - An untyped longword context parameter passed to the driver's + execution routine. + +Return Value: + + Returns STATUS_SUCCESS unless too many map registers are requested. + +Notes: + + Note that this routine MUST be invoked at DISPATCH_LEVEL or above. + +--*/ + +{ + IO_ALLOCATION_ACTION action; + + // + // Make sure the adapter if free. + // + + if (AdapterObject->AdapterInUse) { + ScsiDebugPrint(1,"IoAllocateAdapterChannel: Called while adapter in use.\n"); + } + + // + // Make sure there are enough map registers. + // + + if (NumberOfMapRegisters > AdapterObject->MapRegistersPerChannel) { + + ScsiDebugPrint(1,"IoAllocateAdapterChannel: Out of map registers.\n"); + return(STATUS_INSUFFICIENT_RESOURCES); + } + + action = ExecutionRoutine( DeviceObject, + DeviceObject->CurrentIrp, + AdapterObject->MapRegisterBase, + Context ); + + // + // If the driver wishes to keep the map registers then + // increment the current base and decrease the number of existing map + // registers. + // + + if (action == DeallocateObjectKeepRegisters) { + + AdapterObject->MapRegistersPerChannel -= NumberOfMapRegisters; + (PTRANSLATION_ENTRY) AdapterObject->MapRegisterBase += + NumberOfMapRegisters; + + } else if (action == KeepObject) { + + AdapterObject->AdapterInUse = TRUE; + + } + + return(STATUS_SUCCESS); + +} + +VOID +IoFreeMapRegisters( + PADAPTER_OBJECT AdapterObject, + PVOID MapRegisterBase, + ULONG NumberOfMapRegisters + ) +/*++ + +Routine Description: + + This routine deallocates the map registers for the adapter. If there are + any queued adapter waiting for an attempt is made to allocate the next + entry. + +Arguments: + + AdapterObject - The adapter object to where the map register should be + returned. + + MapRegisterBase - The map register base of the registers to be deallocated. + + NumberOfMapRegisters - The number of registers to be deallocated. + +Return Value: + + None + +--+*/ + +{ + PTRANSLATION_ENTRY translationEntry; + + // + // Determine if this was the last allocation from the adapter. If is was + // then free the map registers by restoring the map register base and the + // channel count; otherwise the registers are lost. This handles the + // normal case. + // + ScsiDebugPrint(2,"IoFreeMapRegisters enter routine\n"); + + translationEntry = AdapterObject->MapRegisterBase; + translationEntry -= NumberOfMapRegisters; + + if (translationEntry == MapRegisterBase) { + + // + // The last allocated registers are being freed. + // + + AdapterObject->MapRegisterBase = (PVOID) translationEntry; + AdapterObject->MapRegistersPerChannel += NumberOfMapRegisters; + } +} + +VOID +IoFreeAdapterChannel( + IN PADAPTER_OBJECT AdapterObject + ) + +/*++ + +Routine Description: + + This routine is invoked to deallocate the specified adapter object. + Any map registers that were allocated are also automatically deallocated. + No checks are made to ensure that the adapter is really allocated to + a device object. However, if it is not, then kernel will bugcheck. + + If another device is waiting in the queue to allocate the adapter object + it will be pulled from the queue and its execution routine will be + invoked. + +Arguments: + + AdapterObject - Pointer to the adapter object to be deallocated. + +Return Value: + + None. + +--*/ + +{ + AdapterObject->AdapterInUse = FALSE; +} + +PHYSICAL_ADDRESS +IoMapTransfer( + IN PADAPTER_OBJECT AdapterObject, + IN PMDL Mdl, + IN PVOID MapRegisterBase, + IN PVOID CurrentVa, + IN OUT PULONG Length, + IN BOOLEAN WriteToDevice + ) + +/*++ + +Routine Description: + + This routine is invoked to set up the map registers in the DMA controller + to allow a transfer to or from a device. + +Arguments: + + AdapterObject - Pointer to the adapter object representing the DMA + controller channel that has been allocated. + + Mdl - Pointer to the MDL that describes the pages of memory that are + being read or written. + + MapRegisterBase - The address of the base map register that has been + allocated to the device driver for use in mapping the transfer. + + CurrentVa - Current virtual address in the buffer described by the MDL + that the transfer is being done to or from. + + Length - Supplies the length of the transfer. This determines the + number of map registers that need to be written to map the transfer. + Returns the length of the transfer which was actually mapped. + + WriteToDevice - Boolean value that indicates whether this is a write + to the device from memory (TRUE), or vice versa. + +Return Value: + + Returns the logical address to be used by bus masters. + +--*/ + +{ + PTRANSLATION_ENTRY DmaMapRegister = MapRegisterBase; + PULONG PageFrameNumber; + ULONG NumberOfPages; + PHYSICAL_ADDRESS Offset; + ULONG i; + + // + // Begin by determining where in the buffer this portion of the operation + // is taking place. + // + + Offset.LowPart = BYTE_OFFSET( (PCHAR) CurrentVa - (PCHAR) Mdl->StartVa ); + Offset.HighPart = 0; + + PageFrameNumber = (PULONG) (Mdl + 1); + NumberOfPages = (Offset.LowPart + *Length + PAGE_SIZE - 1) >> PAGE_SHIFT; + + ScsiDebugPrint(1,"IoMapTransfer %ld pages\n",NumberOfPages); + + PageFrameNumber += (((PCHAR) CurrentVa - (PCHAR) Mdl->StartVa) >> PAGE_SHIFT); + + for (i = 0; i < NumberOfPages; i++) { + ScsiDebugPrint(1,"Mapping %lx at address %lx\n",*PageFrameNumber << PAGE_SHIFT, DmaMapRegister); + (DmaMapRegister++)->PageFrame = (ULONG) *PageFrameNumber++ << PAGE_SHIFT; + } + + + // + // Set the offset to point to the map register plus the offset. + // + + Offset.LowPart += ((PTRANSLATION_ENTRY) MapRegisterBase - (PTRANSLATION_ENTRY) + (READ_REGISTER_ULONG(&DMA_CONTROL->TranslationBase.Long) | KSEG1_BASE) << PAGE_SHIFT); + + // + // Invalidate the translation entry. + // + + WRITE_REGISTER_ULONG(&DMA_CONTROL->TranslationInvalidate.Long, 1); + + + if ( AdapterObject == NULL) { + return(Offset); + } + + if (AdapterObject->PagePort == NULL) { + // + // Internal channels not supported + // + return Offset; + } + + return(Offset); +} + +BOOLEAN +IoFlushAdapterBuffers( + IN PADAPTER_OBJECT AdapterObject, + IN PMDL Mdl, + IN PVOID MapRegisterBase, + IN PVOID CurrentVa, + IN ULONG Length, + IN BOOLEAN WriteToDevice + ) + +/*++ + +Routine Description: + + This routine flushes the DMA adpater object buffers. For the Jazz system + its clears the enable flag which aborts the dma. + +Arguments: + + AdapterObject - Pointer to the adapter object representing the DMA + controller channel. + + Mdl - A pointer to a Memory Descriptor List (MDL) that maps the locked-down + buffer to/from which the I/O occured. + + MapRegisterBase - A pointer to the base of the map registers in the adapter + or DMA controller. + + CurrentVa - The current virtual address in the buffer described the the Mdl + where the I/O operation occurred. + + Length - Supplies the length of the transfer. + + WriteToDevice - Supplies a BOOLEAN value that indicates the direction of + the data transfer was to the device. + +Return Value: + + TRUE - If the transfer was successful. + + FALSE - If there was an error in the transfer. + +--*/ +{ + ULONG i; + ULONG wordPtr, j; + KIRQL irql; + UCHAR DataByte; + + ScsiDebugPrint(2,"IoFlushAdapterBuffers enter routine\n"); + + if (AdapterObject == NULL) { + return TRUE; + } + if (AdapterObject->PagePort) { + + // + // If this is a master channel, then just return since the DMA + // request does not need to be disabled. + // + + DataByte = AdapterObject->AdapterMode; + + if (((PDMA_EISA_MODE) &DataByte)->RequestMode == CASCADE_REQUEST_MODE) { + + return(TRUE); + + } + + return (FALSE); + + } else { + + // + // This would be an internal adapter which is not supported. + // + return(TRUE); + } + +} + +PHYSICAL_ADDRESS +MmGetPhysicalAddress ( + IN PVOID BaseAddress + ) + +/*++ + +Routine Description: + + This function returns the corresponding physical address for a + valid virtual address. + +Arguments: + + BaseAddress - Supplies the virtual address for which to return the + physical address. + +Return Value: + + Returns the corresponding physical address. + +Environment: + + Kernel mode. Any IRQL level. + +--*/ + +{ + PHYSICAL_ADDRESS PhysicalAddress; + + PhysicalAddress.HighPart = 0; + PhysicalAddress.LowPart = (ULONG)BaseAddress & 0x1fffffff; + return(PhysicalAddress); +} + +PVOID +MmAllocateNonCachedMemory ( + IN ULONG NumberOfBytes + ) + +/*++ + +Routine Description: + + This function allocates a range of noncached memory in + the non-paged portion of the system address space. + + This routine is designed to be used by a driver's initialization + routine to allocate a noncached block of virtual memory for + various device specific buffers. + +Arguments: + + NumberOfBytes - Supplies the number of bytes to allocate. + +Return Value: + + NULL - the specified request could not be satisfied. + + NON-NULL - Returns a pointer (virtual address in the nonpaged portion + of the system) to the allocated physically contiguous + memory. + +Environment: + + Kernel mode, IRQL of APC_LEVEL or below. + +--*/ + +{ + PVOID BaseAddress; + + // + // Allocated the memory. + // + + BaseAddress = FwAllocatePool(NumberOfBytes); + + // + // Make it non-cached. + // + + BaseAddress = (PVOID)((ULONG) BaseAddress | KSEG1_BASE); + return BaseAddress; +} + +PVOID +MmMapIoSpace ( + IN PHYSICAL_ADDRESS PhysicalAddress, + IN ULONG NumberOfBytes, + IN BOOLEAN CacheEnable + ) + +/*++ + +Routine Description: + + This function returns the corresponding virtual address for a + known physical address. + +Arguments: + + PhysicalAddress - Supplies the phiscal address. + + NumberOfBytes - Unused. + + CacheEnable - Unused. + +Return Value: + + Returns the corresponding virtual address. + +Environment: + + Kernel mode. Any IRQL level. + +--*/ + +{ + PCCHAR VirtualAddress; + + switch ((ULONG) PAGE_ALIGN(PhysicalAddress.LowPart)) { +#ifdef DUO + case SCSI1_PHYSICAL_BASE: + VirtualAddress = (PVOID) SCSI1_VIRTUAL_BASE; + break; + case SCSI2_PHYSICAL_BASE: + VirtualAddress = (PVOID) SCSI2_VIRTUAL_BASE; + break; +#endif + + case EISA_CONTROL_PHYSICAL_BASE: + VirtualAddress = (PVOID) EISA_IO_VIRTUAL_BASE; + break; + case DMA_PHYSICAL_BASE: + VirtualAddress = (PVOID) DMA_VIRTUAL_BASE; + break; + default: + if (PhysicalAddress.LowPart > EISA_MEMORY_PHYSICAL_BASE) { + VirtualAddress = (PVOID) EISA_MEMORY_VIRTUAL_BASE; + VirtualAddress += PhysicalAddress.LowPart&0xFFFFFF; + return(VirtualAddress); + } + return(NULL); + } + + VirtualAddress += BYTE_OFFSET(PhysicalAddress.LowPart); + + return(VirtualAddress); +} + +PADAPTER_OBJECT +HalpAllocateEisaAdapter( + IN PDEVICE_DESCRIPTION DeviceDescriptor + ) +/*++ + +Routine Description: + + This function allocates an EISA adapter object according to the + specification supplied in the device description. The necessary device + descriptor information is saved. If there is + no existing adapter object for this channel then a new one is allocated. + The saved information in the adapter object is used to set the various DMA + modes when the channel is allocated or a map transfer is done. + +Arguments: + + DeviceDescription - Supplies the description of the device which want to + use the DMA adapter. + +Return Value: + + Returns a pointer to the newly created adapter object or NULL if one + cannot be created. + +--*/ + +{ + PADAPTER_OBJECT adapterObject; + UCHAR adapterMode; + + // + // Check if it's an Eisa and master + // + + if ((DeviceDescriptor->InterfaceType != Eisa) || + (DeviceDescriptor->Master == FALSE)) { + return(NULL); + } + + // + // Allocate an adapter object. + // + + adapterObject = (PADAPTER_OBJECT) IopAllocateAdapter( + 0, + 0, // adapter base va + NULL + ); + + if (adapterObject == NULL) { + + return(NULL); + + } + + + // + // Indicate this is an Eisa bus master by setting the page port + // and mode to cascade even though it is not used. + // And start IO mapping at virutal Adr 0x180000. + // + + adapterObject->PagePort = (PVOID) (~0x0); + adapterMode = adapterObject->AdapterMode; + ((PDMA_EISA_MODE) &adapterMode)->RequestMode = CASCADE_REQUEST_MODE; + adapterObject->MapRegisterBase = (PVOID) ((PTRANSLATION_ENTRY)adapterObject->MapRegisterBase + (0x180000>>PAGE_SHIFT)); + + return(adapterObject); +} diff --git a/private/ntos/fw/duobase/mips/romsetup.c b/private/ntos/fw/duobase/mips/romsetup.c new file mode 100644 index 000000000..d843cf38b --- /dev/null +++ b/private/ntos/fw/duobase/mips/romsetup.c @@ -0,0 +1,1097 @@ +/*++ + +Copyright (c) 1993 Microsoft Corporation + +Module Name: + + romsetup.c + +Abstract: + + This is the setup program to update the DUO flash prom + +Author: + + Lluis Abello (lluis) + +Environment: + + ARC Firmware + +Notes: + + This module must be linked between 2MB and 4 MB Kesg0 addresses. + The executable must be non relocatable. + +Revision History: + +--*/ + +#include "fwp.h" +#include "duobase.h" +#ifndef DUO +#define FLASH_ENABLE_VIRTUAL_BASE 0xE000D500 +#endif + +#define KeGetDcacheFillSize() 64 + +ULONG +InitKeyboardController( + ); + +ULONG +InitKeyboard( + ); + +BOOLEAN +SendKbdCommand( + IN UCHAR Command + ); + +BOOLEAN +GetKbdData( + PUCHAR C, + ULONG msec + ); + +BOOLEAN +SendKbdData( + IN UCHAR Data + ); + +UCHAR KbdBuffer[3]; +ULONG KbdBufferWIndex = 0; +ULONG KbdBufferRIndex = 0; + +// +// Keyboard static variables. +// + +BOOLEAN KbdCtrl = FALSE; +BOOLEAN Scan0xE0 = FALSE; + +// 1111 1 1111122222222 2 2333333333344 4 44444445555 5 5 55 +// Character # 234567890123 4 5678901234567 8 9012345678901 2 34567890123 4 5 67 +PCHAR NormalLookup = "1234567890-=\b\0qwertyuiop[]\n\0asdfghjkl;'`\0\\zxcvbnm,./\0\0\0 "; +PCHAR ShiftedLookup = "!@#$%^&*()_+\b\0QWERTYUIOP{}\n\0ASDFGHJKL:\"~\0\|ZXCVBNM<>?\0\0\0 "; + +BOOLEAN FwLeftShift = FALSE; +BOOLEAN FwRightShift = FALSE; +BOOLEAN FwControl = FALSE; +BOOLEAN FwAlt = FALSE; +BOOLEAN FwCapsLock = FALSE; + + + +#define StoreKeyboardChar(C) KbdBuffer[KbdBufferWIndex++] = C + +extern ULONG end; + +PULONG ImageBuffer; + +ULONG ImageSize; + +CHAR +GetChar( + IN VOID + ); + +VOID +LoadProm( + IN PCHAR DiskName + ); + + +BOOLEAN +ReadRawFile( + IN PUCHAR Name + ); + +BOOLEAN +ProgramFlashProm( + IN PULONG ImageBuffer, + IN ULONG ImageSize + ); + +VOID +ResetSystem ( + IN VOID + ); + +VOID +WaitForKey( + ); + +VOID +Romsetup( + IN ULONG Argc, + IN PCHAR *Argv + ) + +/*++ + +Routine Description: + + This is the entry point of the executable that is loaded by the base + prom. + This routine does the following: + Initializes the display. + Loads the file with the Flash Prom image. + Erases the Flash prom. + Puts the loaded image in the flash prom. + Resets the machine. + +Arguments: + + Argc - Supplies the number of arguments. + Argv - Supplies the arguments + The first argument supplies the arc pathname of the floppy disk + If the program was loaded from "run a program" then the path also + contains the name of the executable. + +Return Value: + + Never returns. Resets the machine. + +--*/ + +{ + UCHAR PathName[128]; + PCHAR Choices[2]; + LONG DefaultChoice = 1; + LONG NumberOfMenuChoices = 2; + LONG Index; + UCHAR Character; + PCHAR Pointer, EndPointer; + + DisplayBootInitialize(); + FwSetScreenColor( ArcColorWhite, ArcColorBlue); + FwSetScreenAttributes( TRUE, FALSE, FALSE); + FwClearScreen(); + + // + // Initialize the keyboard. + // + + InitKeyboardController(); + InitKeyboard(); + + // + // Extract the arc disk name from Argv[0] if it has a file name atached. + // Copy the string and put a '\0' after the last ')' is found. + // + + strcpy(PathName,Argv[0]); + + Pointer = EndPointer = PathName; + while (*Pointer) { + if (*Pointer == ')') { + EndPointer = Pointer; + } + Pointer++; + } + EndPointer++; + *EndPointer = '\0'; + + // + // Clear translation mode. + // + SendKbdCommand(KBD_CTR_READ_COMMAND); + GetKbdData(&Character,100); + Character = (Character & 0xBF); + SendKbdCommand(KBD_CTR_WRITE_COMMAND); + SendKbdData(Character); + + Choices[0] = "Update PROM"; + Choices[1] = "Exit"; + + while (TRUE) { + // + // Display the menu. + // + FwSetPosition( 0, 0); + FwPrint("Duo EEPROM Update program. Version 1.0"); + FwSetPosition( 3, 0); + FwPrint(" Actions:\r\n"); + + for (Index = 0; Index < NumberOfMenuChoices; Index++ ) { + FwSetPosition( Index + 5, 5); + + if (Index == DefaultChoice) { + FwSetScreenAttributes( TRUE, FALSE, TRUE); + } + + FwPrint(Choices[Index]); + FwSetScreenAttributes( TRUE, FALSE, FALSE); + } + FwSetPosition(NumberOfMenuChoices + 6, 0); + FwPrint(" Use the arrow keys to select.\r\n"); + FwPrint(" Press Enter to choose.\r\n"); + FwPrint("\r\n"); + // + // Display the bitmap. + // + + FwSetScreenColor( ArcColorCyan, ArcColorBlue); + JxBmp(); + FwSetScreenColor( ArcColorWhite, ArcColorBlue); + + // + // Get input + // + do { + Character = GetChar(); + switch (Character) { + + case ASCII_ESC: + Character = GetChar(); + if (Character != '[') { + break; + } + + case ASCII_CSI: + Character = GetChar(); + FwSetPosition( DefaultChoice + 5, 5); + FwPrint(Choices[DefaultChoice]); + switch (Character) { + case 'A': + case 'D': + DefaultChoice--; + if (DefaultChoice < 0) { + DefaultChoice = NumberOfMenuChoices-1; + } + break; + case 'B': + case 'C': + DefaultChoice++; + if (DefaultChoice == NumberOfMenuChoices) { + DefaultChoice = 0; + } + break; + case 'H': + DefaultChoice = 0; + break; + default: + break; + } + FwSetPosition( DefaultChoice + 5, 5); + FwSetScreenAttributes( TRUE, FALSE, TRUE); + FwPrint(Choices[DefaultChoice]); + FwSetScreenAttributes( TRUE, FALSE, FALSE); + continue; + + default: + break; + } + + + } while ((Character != '\n') && (Character != '\r')); + if (DefaultChoice == 0) { + + FwSetPosition(NumberOfMenuChoices + 6, 0); + FwClearLine(); + FwSetPosition(NumberOfMenuChoices + 7, 0); + FwClearLine(); + FwSetPosition(NumberOfMenuChoices + 6, 0); + + strcat(PathName,"duoprom.raw"); + if (ReadRawFile(PathName) == TRUE) { + if (ProgramFlashProm(ImageBuffer,ImageSize) == FALSE) { + FwPrint("The EEPROM update process failed.\r\nPress any key to continue."); + WaitForKey(); + } + } else { + FwPrint("The EEPROM has not been updated.\r\nPress any key to continue."); + WaitForKey(); + } + + FwSetPosition(NumberOfMenuChoices + 6, 0); + FwClearLine(); + FwSetPosition(NumberOfMenuChoices + 7, 0); + FwClearLine(); + FwSetPosition(NumberOfMenuChoices + 8, 0); + FwClearLine(); + FwSetPosition(NumberOfMenuChoices + 9, 0); + FwClearLine(); + } + if (DefaultChoice == 2) { + strcpy(PathName,"scsi()disk(2)fdisk()"); + LoadProm(PathName); + } + + if (DefaultChoice == 1) { + ResetSystem(); + } + } +} + +VOID +LoadProm( + IN PCHAR DiskName + ) +/*++ + +Routine Description: + + This routine opens the root directory and finds the files + with extension ".RAW" if there is more than one, a menu + is display and the user can choose the file to load, otherwise + the file found is loaded. If there are no files with the .RAW + extension an error message is printed. + +Arguments: + + DiskName Arc path name of the floppy disk. + +Return Value: + + None. + +--*/ + +{ + DIRECTORY_ENTRY DirEntry[8]; + LONG Index; + ULONG Fid; + LONG Count = 8; + ARC_STATUS Status; + LONG DefaultChoice = 0; + LONG NumberOfMenuChoices = 0; + CHAR PathName[64]; + CHAR Choices[7][13]; + UCHAR Character; + + // + // Open the disk root + // + strcat(DiskName,"\\"); + if (Status = ArcOpen(DiskName,ArcOpenReadOnly,&Fid) != ESUCCESS) { + FwPrint("Error opening %s. Status %lx\r\n",DiskName,Status); + WaitForKey(); + + FwPrint("Attempting to load duoprom.raw\r\n"); + strcpy(PathName,DiskName); + strcat(PathName,"duoprom.raw"); + if (ReadRawFile(PathName) == TRUE) { + ProgramFlashProm(ImageBuffer,ImageSize); + return; + } else { + FwPrint("File Error.\r\nThe EEPROM has not been updated.\r\nPress any key to continue."); + WaitForKey(); + return; + } + + } + + // + // Scan for files with .raw extension and build the menu choices. + // + while ((Count == 8) && (NumberOfMenuChoices != 6)) { + if (Status = ArcGetDirectoryEntry(Fid,DirEntry,7,&Count) != ESUCCESS) { + FwPrint("Error reading directory. Status %lx\r\n",Status); + break; + } else { + + // + // Build the choices. + // + for (Index=0;Index<Count;Index++) { + // + // Terminate the file name string + // + DirEntry[Index].FileName[DirEntry[Index].FileNameLength] = '\0'; + if (strstr(DirEntry[Index].FileName,".raw")) { + strcpy (Choices[NumberOfMenuChoices],DirEntry[Index].FileName); + NumberOfMenuChoices++; + if (NumberOfMenuChoices == 6) { + break; + } + } + } + } + } + + ArcClose(Fid); + + FwSetScreenAttributes( TRUE, FALSE, FALSE); + FwClearScreen(); + FwSetPosition( 3, 0); + + // + // No files found. Print error and exit. + // + if (NumberOfMenuChoices == 0) { + FwPrint("Image File not found.\r\nPress any key to continue."); + WaitForKey(); + return; + } + // + // One file found. Load it and update prom. + // + if (NumberOfMenuChoices == 1) { + strcpy(PathName,DiskName); + strcat(PathName,Choices[0]); + if (ReadRawFile(PathName) == TRUE) { + ProgramFlashProm(ImageBuffer,ImageSize); + return; + } else { + FwPrint("File Error.\r\nThe EEPROM has not been updated.\r\nPress any key to continue."); + WaitForKey(); + return; + } + } + + + strcpy(Choices[NumberOfMenuChoices++],"Exit"); + while (TRUE) { + // + // Display the menu. + // + FwSetPosition( 3, 0); + FwPrint(" Files:\r\n"); + + for (Index = 0; Index < NumberOfMenuChoices; Index++ ) { + FwSetPosition( Index + 5, 5); + + if (Index == DefaultChoice) { + FwSetScreenAttributes( TRUE, FALSE, TRUE); + } + + FwPrint(Choices[Index]); + FwSetScreenAttributes( TRUE, FALSE, FALSE); + } + FwSetPosition(NumberOfMenuChoices + 6, 0); + FwPrint(" Use the arrow keys to select.\r\n"); + FwPrint(" Press Enter to choose.\r\n"); + FwPrint("\r\n"); + // + // Display the bitmap. + // + + FwSetScreenColor( ArcColorCyan, ArcColorBlue); + JxBmp(); + FwSetScreenColor( ArcColorWhite, ArcColorBlue); + + // + // Get input + // + do { + Character = GetChar(); + switch (Character) { + + case ASCII_ESC: + Character = GetChar(); + if (Character != '[') { + break; + } + + case ASCII_CSI: + Character = GetChar(); + FwSetPosition( DefaultChoice + 5, 5); + FwPrint(Choices[DefaultChoice]); + switch (Character) { + case 'A': + case 'D': + DefaultChoice--; + if (DefaultChoice < 0) { + DefaultChoice = NumberOfMenuChoices-1; + } + break; + case 'B': + case 'C': + DefaultChoice++; + if (DefaultChoice == NumberOfMenuChoices) { + DefaultChoice = 0; + } + break; + case 'H': + DefaultChoice = 0; + break; + default: + break; + } + FwSetPosition( DefaultChoice + 5, 5); + FwSetScreenAttributes( TRUE, FALSE, TRUE); + FwPrint(Choices[DefaultChoice]); + FwSetScreenAttributes( TRUE, FALSE, FALSE); + continue; + + default: + break; + } + + + } while ((Character != '\n') && (Character != '\r')); + + // + // Exit. + // + + if (DefaultChoice == NumberOfMenuChoices - 1) { + return; + } else { + FwSetPosition(NumberOfMenuChoices + 6, 0); + FwClearLine(); + FwSetPosition(NumberOfMenuChoices + 7, 0); + FwClearLine(); + strcpy(PathName,DiskName); + strcat(PathName,Choices[DefaultChoice]); + if (ReadRawFile(PathName) == TRUE) { + if (ProgramFlashProm(ImageBuffer,ImageSize) == FALSE) { + FwPrint("The EEPROM update process failed.\r\nPress any key to continue."); + } + } else { + FwPrint("The EEPROM has not been updated.\r\nPress any key to continue."); + } + + FwSetPosition(NumberOfMenuChoices + 6, 0); + FwClearLine(); + FwSetPosition(NumberOfMenuChoices + 7, 0); + FwClearLine(); + FwSetPosition(NumberOfMenuChoices + 8, 0); + FwClearLine(); + FwSetPosition(NumberOfMenuChoices + 9, 0); + FwClearLine(); + + } + } +} + +VOID +WaitForKey( + ) + +{ + UCHAR Result; + + for (;;) { + if (!GetKbdData(&Result,200)) { + if (!(Result & 0x80)) { + return; + } + } + } +} + + + +BOOLEAN +ProgramFlashProm( + IN PULONG ImageBuffer, + IN ULONG ImageSize + ) + +/*++ + +Routine Description: + + This routine Erases the Flash prom. And programs it with the + image loaded at ImageBuffer. + +Arguments: + + ImageBuffer - Pointer to the loaded image. + + ImageSize - Size in bytes of ImageBuffer; + +Return Value: + + TRUE if the PROM was successfully updated, + FALSE otherwise. + + +--*/ + +{ + + ULONG Count; + UCHAR Byte; + PUCHAR UcharImage; + ULONG DataLong; + + FwPrint("Erasing prom."); + // + // Enable 12v and wait 1ms for it to stabilize. + // + WRITE_REGISTER_ULONG(FLASH_ENABLE_VIRTUAL_BASE,1); + FwStallExecution(1000); + + // + // Enable Erase. + // + WRITE_REGISTER_UCHAR(EEPROM_VIRTUAL_BASE,0x30); + WRITE_REGISTER_UCHAR(EEPROM_VIRTUAL_BASE,0x30); + + // + // Wait for PROM to be erased. + // + for (Count = 0; Count < 6000; Count++) { + FwStallExecution(10000); + Byte = READ_REGISTER_UCHAR(EEPROM_VIRTUAL_BASE); + if ((Count & 0x1FF) == 0) { + FwPrint("."); + } + if ((Byte & 0x80) != 0 ) { + break; + } + } + if ((Byte & 0x80) == 0 ) { + FwPrint("Unable to erase prom.\r\n"); + return FALSE; + } + FwPrint("\r\nProgramming prom."); + + UcharImage = (PUCHAR)ImageBuffer; + for (Count = 0; Count < ImageSize; Count++) { + WRITE_REGISTER_UCHAR(EEPROM_VIRTUAL_BASE+Count,0x50); + WRITE_REGISTER_UCHAR(EEPROM_VIRTUAL_BASE+Count,UcharImage[Count]); + + do { + Byte = READ_REGISTER_UCHAR(EEPROM_VIRTUAL_BASE+Count); + } while (Byte != UcharImage[Count]); + + if ((Count & 0xFFFF) == 0) { + FwPrint("."); + } + } + // + // Disable 12v and wait 1ms for it to stabilize. + // + WRITE_REGISTER_ULONG(FLASH_ENABLE_VIRTUAL_BASE,0); + FwStallExecution(1000); + FwPrint("\r\nVerifying prom."); + for (Count = 0; Count < ImageSize/sizeof(ULONG); Count++) { + DataLong = READ_REGISTER_ULONG(EEPROM_VIRTUAL_BASE+Count*sizeof(ULONG)); + if (ImageBuffer[Count] != DataLong) { + FwPrint("..Error\r\n"); + return FALSE; + } + if ((Count & 0x3FFF) == 0) { + FwPrint("."); + } + } + return TRUE; +} + +BOOLEAN +ReadRawFile( + IN PUCHAR Name + ) + +{ + ULONG Fid; + ULONG Count; + ARC_STATUS Status; + PULONG EndOfBuffer; + PULONG PBuffer; + ULONG CheckSum; + FILE_INFORMATION FileInfo; + + ImageBuffer = &end; + ImageBuffer = (PVOID) ((ULONG) ((PCHAR) ImageBuffer + KeGetDcacheFillSize() - 1) + & ~(KeGetDcacheFillSize() - 1)); + + FwPrint("Loading Image."); + // + // Open the raw file. + // + if (Status = ArcOpen(Name,ArcOpenReadOnly,&Fid) != ESUCCESS) { + FwPrint(" File Open Error %lx\r\n", Status); + return FALSE; + } + + if (Status = ArcGetFileInformation(Fid,&FileInfo) != ESUCCESS) { + FwPrint(" FileInformation error %lx\r\n",Status); + return FALSE; + } + + FwPrint("."); + ImageSize = FileInfo.EndingAddress.LowPart; +/* + Status = ArcRead(Fid, ImageBuffer,ImageSize,&Count); + FwPrint(".\r\n"); + if (Status != ESUCCESS) { + ArcClose(Fid); + return FALSE; + } +*/ + Status = ArcRead(Fid, ImageBuffer,0x10000,&Count); + if (Status != ESUCCESS) { + ArcClose(Fid); + return FALSE; + } + FwPrint("."); + Status = ArcRead(Fid, ImageBuffer+0x10000/sizeof(ULONG),0x10000,&Count); + if (Status != ESUCCESS) { + ArcClose(Fid); + return FALSE; + } + FwPrint("."); + Status = ArcRead(Fid, ImageBuffer+0x20000/sizeof(ULONG),0x10000,&Count); + if (Status != ESUCCESS) { + ArcClose(Fid); + return FALSE; + } + FwPrint("."); + Status = ArcRead(Fid, ImageBuffer+0x30000/sizeof(ULONG),0x10000,&Count); + if (Status != ESUCCESS) { + ArcClose(Fid); + return FALSE; + } + FwPrint("."); + + ArcClose(Fid); + + // + // Check the Rom checksum in the loaded image; + // + CheckSum = 0; + + EndOfBuffer = ImageBuffer + ImageSize/sizeof(ULONG); + PBuffer = ImageBuffer; + + do { + CheckSum += *PBuffer++; + } while (PBuffer != EndOfBuffer); + + if (CheckSum == 0) { + return TRUE; + } else { + FwPrint("Incorrect Checksum.\r\n"); + return FALSE; + } +} + +VOID +ResetSystem ( + IN VOID + ) +/*++ + +Routine Description: + + This routine resets the system by asserting the reset line + from the keyboard controller. + +Arguments: + + None. + +Return Value: + + None. + +--*/ +{ + SendKbdCommand(0xD1); + SendKbdData(0); +} + + +VOID +TranslateScanCode( + IN UCHAR Scan + ) + +/*++ + +Routine Description: + + This routine translates the given keyboard scan code into an + ASCII character and puts it in the circular buffer. + +Arguments: + + Scan - Supplies the scan code read from the keyboard. + +Return Value: + + TRUE if a complete character can be returned. + +--*/ + +{ + UCHAR FwControlCharacter=0; + UCHAR FwFunctionCharacter; + BOOLEAN MakeCode; + UCHAR Char; + + // + // Check 0xE0, which introduces a two key sequence. + // + + if (Scan == 0xE0) { + Scan0xE0 = TRUE; + return; + } + if (Scan0xE0 == TRUE) { + // + // Check for PrintScrn (used as SysRq, also found in its true Alt + // form below). + // + if (Scan == KEY_PRINT_SCREEN) { + StoreKeyboardChar(ASCII_SYSRQ); + Scan0xE0 = FALSE; + return; + } + } + + // + // Look for scan codes that indicate shift, control, or alt keys. Bit 7 + // of scan code indicates upward or downward keypress. + // + MakeCode = !(Scan & 0x80); + switch (Scan & 0x7F) { + + case KEY_LEFT_SHIFT: + FwLeftShift = MakeCode; + return; + + case KEY_RIGHT_SHIFT: + FwRightShift = MakeCode; + return; + + case KEY_CONTROL: + FwControl = MakeCode; + return; + + case KEY_ALT: + FwAlt = MakeCode; + return; + + default: + break; + + } + + // + // The rest of the keys only do something on make. + // + + if (MakeCode) { + + // + // Check for control keys. + // + + switch (Scan) { + + case KEY_UP_ARROW: + FwControlCharacter = 'A'; + break; + + case KEY_DOWN_ARROW: + FwControlCharacter = 'B'; + break; + + case KEY_RIGHT_ARROW: + FwControlCharacter = 'C'; + break; + + case KEY_LEFT_ARROW: + FwControlCharacter = 'D'; + break; + + case KEY_HOME: + FwControlCharacter = 'H'; + break; + + case KEY_END: + FwControlCharacter = 'K'; + break; + + case KEY_PAGE_UP: + FwControlCharacter = '?'; + break; + + case KEY_PAGE_DOWN: + FwControlCharacter = '/'; + break; + + case KEY_INSERT: + FwControlCharacter = '@'; + break; + + case KEY_DELETE: + FwControlCharacter = 'P'; + break; + + case KEY_SYS_REQUEST: + StoreKeyboardChar(ASCII_SYSRQ); + return; + + case KEY_ESC: + StoreKeyboardChar(ASCII_ESC); + return; + + case KEY_CAPS_LOCK: + FwCapsLock = !FwCapsLock; + return; + + case KEY_F1: + FwControlCharacter = 'O'; + FwFunctionCharacter = 'P'; + break; + + case KEY_F2: + FwControlCharacter = 'O'; + FwFunctionCharacter = 'Q'; + break; + + case KEY_F3: + FwControlCharacter = 'O'; + FwFunctionCharacter = 'w'; + break; + + case KEY_F4: + FwControlCharacter = 'O'; + FwFunctionCharacter = 'x'; + break; + + case KEY_F5: + FwControlCharacter = 'O'; + FwFunctionCharacter = 't'; + break; + + case KEY_F6: + FwControlCharacter = 'O'; + FwFunctionCharacter = 'u'; + break; + + case KEY_F7: + FwControlCharacter = 'O'; + FwFunctionCharacter = 'q'; + break; + + case KEY_F8: + FwControlCharacter = 'O'; + FwFunctionCharacter = 'r'; + break; + + case KEY_F9: + FwControlCharacter = 'O'; + FwFunctionCharacter = 'p'; + break; + + case KEY_F10: + FwControlCharacter = 'O'; + FwFunctionCharacter = 'M'; + break; + +// case KEY_F11: +// FwControlCharacter = 'O'; +// FwFunctionCharacter = 'A'; +// break; +// +// case KEY_F12: +// FwControlCharacter = 'O'; +// FwFunctionCharacter = 'B'; +// break; + + default: + Char = 0; + + // + // Check to see if the scan code corresponds to an ASCII + // character. + // + + if (((Scan >= 16) && (Scan <= 25)) || + ((Scan >= 30) && (Scan <= 38)) || + ((Scan >= 44) && (Scan <= 50))) { + if (((FwLeftShift || FwRightShift) && !FwCapsLock) || + (!(FwLeftShift || FwRightShift) && FwCapsLock)) { + Char = ShiftedLookup[Scan - 2]; + } else { + Char = NormalLookup[Scan - 2]; + } + } else { + if ((Scan > 1) && (Scan < 58)) { + + // + // Its ASCII but not alpha, so don't shift on CapsLock. + // + + if (FwLeftShift || FwRightShift) { + Char = ShiftedLookup[Scan - 2]; + } else { + Char = NormalLookup[Scan - 2]; + } + } + } + + // + // If a character, store it in buffer. + // + + if (Char) { + StoreKeyboardChar(Char); + return; + } + break; + } + if (FwControlCharacter) { + StoreKeyboardChar(ASCII_CSI); + StoreKeyboardChar(FwControlCharacter); + if (FwControlCharacter == 'O') { + StoreKeyboardChar(FwFunctionCharacter); + } + return; + } + } +} + + +CHAR +GetChar( + IN VOID + ) +/*++ + +Routine Description: + + This routine reads a character from the keyboard. + +Arguments: + + None. + +Return Value: + + Character. + +--*/ + +{ + + UCHAR ScanCode; + + // + // Check if we have something in the buffer and return it if we do. + // + while (TRUE) { + if (KbdBufferWIndex) { + ScanCode = KbdBuffer[KbdBufferRIndex]; + KbdBufferRIndex++; + KbdBufferWIndex--; + if (KbdBufferWIndex == 0) { + KbdBufferRIndex = 0; + } + return ScanCode; + } + + // + // There is nothing in the buffer. Wait for a key to be pressed. + // + for (;;) { + if (!GetKbdData(&ScanCode,2000)) { + break; + } + } + + // + // Translate the scan code. + // + TranslateScanCode(ScanCode); + } +} diff --git a/private/ntos/fw/duobase/mips/scsiboot.c b/private/ntos/fw/duobase/mips/scsiboot.c new file mode 100644 index 000000000..328c6284a --- /dev/null +++ b/private/ntos/fw/duobase/mips/scsiboot.c @@ -0,0 +1,4664 @@ +/*++ + +Copyright (c) 1990 Microsoft Corporation + +Module Name: + + scsiboot.c + +Abstract: + + This is the NT SCSI port driver. + +Author: + + Mike Glass + Jeff Havens + +Environment: + + kernel mode only + +Notes: + + This module is linked into the kernel. + +Revision History: + +--*/ + +#include "stdarg.h" +#include "stdio.h" +#include "fwp.h" +#include "scsi.h" +#include "scsiboot.h" +#include "duobase.h" + +ULONG ScsiPortCount; +ULONG ScsiDebug = 0; +PDEVICE_OBJECT ScsiPortDeviceObject[10]; +PINQUIRYDATA InquiryDataBuffer; +FULL_SCSI_REQUEST_BLOCK PrimarySrb; +FULL_SCSI_REQUEST_BLOCK RequestSenseSrb; +FULL_SCSI_REQUEST_BLOCK AbortSrb; + +// +// Function declarations +// + +ARC_STATUS +ScsiPortDispatch( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +VOID +ScsiPortExecute( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +VOID +ScsiPortStartIo ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +BOOLEAN +ScsiPortInterrupt( + IN PKINTERRUPT InterruptObject, + IN PDEVICE_OBJECT DeviceObject + ); + +VOID +ScsiPortCompletionDpc( + IN PKDPC Dpc, + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context + ); + +VOID +ScsiPortTickHandler( + IN PDEVICE_OBJECT DeviceObject, + IN PVOID Context + ); + +IO_ALLOCATION_ACTION +ScsiPortAllocationRoutine ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID MapRegisterBase, + IN PVOID Context + ); + +ARC_STATUS +IssueInquiry( + IN PDEVICE_EXTENSION deviceExtension, + IN PLUNINFO LunInfo + ); + +VOID +IssueRequestSense( + IN PDEVICE_EXTENSION deviceExtension, + IN PSCSI_REQUEST_BLOCK FailingSrb + ); + +VOID +ScsiPortInternalCompletion( + PDEVICE_OBJECT DeviceObject, + PIRP Irp, + PVOID Context + ); + +PSCSI_BUS_SCAN_DATA +ScsiBusScan( + IN PDEVICE_EXTENSION DeviceExtension, + IN UCHAR ScsiBus, + IN UCHAR InitiatorBusId + ); + +PLOGICAL_UNIT_EXTENSION +CreateLogicalUnitExtension( + IN PDEVICE_EXTENSION DeviceExtension + ); + +BOOLEAN +SpStartIoSynchronized ( + PVOID ServiceContext + ); + +VOID +IssueAbortRequest( + IN PDEVICE_EXTENSION DeviceExtension, + IN PIRP FailingIrp + ); + +IO_ALLOCATION_ACTION +SpBuildScatterGather( + IN struct _DEVICE_OBJECT *DeviceObject, + IN struct _IRP *Irp, + IN PVOID MapRegisterBase, + IN PVOID Context + ); + +BOOLEAN +SpGetInterruptState( + IN PVOID ServiceContext + ); + +VOID +SpTimerDpc( + IN PKDPC Dpc, + IN PVOID Context, + IN PVOID SystemContext1, + IN PVOID SystemContext2 + ); + +PLOGICAL_UNIT_EXTENSION +GetLogicalUnitExtension( + PDEVICE_EXTENSION DeviceExtension, + UCHAR TargetId + ); + +NTSTATUS +SpInitializeConfiguration( + IN PDEVICE_EXTENSION DeviceExtension, + IN PHW_INITIALIZATION_DATA HwInitData, + OUT PPORT_CONFIGURATION_INFORMATION ConfigInfo, + IN BOOLEAN InitialCall + ); + +NTSTATUS +SpGetCommonBuffer( + PDEVICE_EXTENSION DeviceExtension, + ULONG NonCachedExtensionSize + ); + + +// +// Routines start +// + +ULONG +ScsiPortInitialize( + IN PVOID Argument1, + IN PVOID Argument2, + IN struct _HW_INITIALIZATION_DATA *HwInitializationData, + IN PVOID HwContext + ) + +/*++ + +Routine Description: + + This routine initializes the port driver. + +Arguments: + + Argument1 - Pointer to driver object created by system + HwInitializationData - Miniport initialization structure + HwContext - Value passed to miniport driver's config routine + +Return Value: + + The function value is the final status from the initialization operation. + +--*/ + +{ + PDEVICE_EXTENSION deviceExtension; + PDEVICE_OBJECT DeviceObject; + PORT_CONFIGURATION_INFORMATION configInfo; + KEVENT allocateAdapterEvent; + ULONG ExtensionAllocationSize; + ULONG j; + UCHAR scsiBus; + PULONG scsiPortNumber; + ULONG numberOfPageBreaks; + PIO_SCSI_CAPABILITIES capabilities; + BOOLEAN callAgain; + DEVICE_DESCRIPTION deviceDescription; + ARC_CODES status; + BOOLEAN foundOne = FALSE; + + UNREFERENCED_PARAMETER(Argument1); + UNREFERENCED_PARAMETER(Argument2); + + if (HwInitializationData->HwInitializationDataSize != sizeof(HW_INITIALIZATION_DATA)) { + + ScsiDebugPrint((0,"ScsiPortInitialize: Miniport driver wrong version\n")); + + return EBADF; + } + + // + // Check that each required entry is not NULL. + // + + if ((!HwInitializationData->HwInitialize) || + (!HwInitializationData->HwFindAdapter) || + (!HwInitializationData->HwInterrupt) || + (!HwInitializationData->HwResetBus)) { + + ScsiDebugPrint((0, + "ScsiPortInitialize: Miniport driver missing required entry\n")); + + return EBADF; + } + +CallAgain: + + // + // Get the configuration information + // + + scsiPortNumber = &ScsiPortCount; + + // + // Determine size of extensions. + // + + ExtensionAllocationSize = DEVICE_EXTENSION_SIZE + + HwInitializationData->DeviceExtensionSize + sizeof(DEVICE_OBJECT); + + DeviceObject = ExAllocatePool(NonPagedPool, ExtensionAllocationSize); + + if (DeviceObject == NULL) { + return ENOMEM; + } + + RtlZeroMemory(DeviceObject, ExtensionAllocationSize); + + // + // Set up device extension pointers + // + + deviceExtension = DeviceObject->DeviceExtension = (PVOID) (DeviceObject + 1); + deviceExtension->DeviceObject = DeviceObject; + + // + // Save the dependent driver routines in the device extension. + // + + deviceExtension->HwInitialize = HwInitializationData->HwInitialize; + deviceExtension->HwStartIo = HwInitializationData->HwStartIo; + deviceExtension->HwInterrupt = HwInitializationData->HwInterrupt; + deviceExtension->HwReset = HwInitializationData->HwResetBus; + deviceExtension->HwDmaStarted = HwInitializationData->HwDmaStarted; + deviceExtension->HwLogicalUnitExtensionSize = + HwInitializationData->SpecificLuExtensionSize; + + deviceExtension->HwDeviceExtension = + (PVOID)(deviceExtension + 1); + + // + // Set indicater as to whether adapter needs kernel mapped buffers. + // + + deviceExtension->MapBuffers = HwInitializationData->MapBuffers; + + // + // Mark this object as supporting direct I/O so that I/O system + // will supply mdls in irps. + // + + DeviceObject->Flags |= DO_DIRECT_IO; + + // + // Check if miniport driver requires any noncached memory. + // SRB extensions will come from zoned memory. A page is + // allocated as it is the smallest unit of noncached memory + // allocation. + // + + deviceExtension->SrbExtensionSize = HwInitializationData->SrbExtensionSize; + + // + // Get the miniport configuration information. + // + + capabilities = &deviceExtension->Capabilities; + + capabilities->Length = sizeof(IO_SCSI_CAPABILITIES); + + callAgain = FALSE; + + + if (!NT_SUCCESS(SpInitializeConfiguration( + deviceExtension, + HwInitializationData, + &configInfo, + TRUE + ))) { + + ScsiDebugPrint((2, "ScsiPortInitialize: no Configuration info found\n")); + return(ENODEV); + } + + configInfo.NumberOfAccessRanges = HwInitializationData->NumberOfAccessRanges; + + configInfo.AccessRanges = ExAllocatePool( + NonPagedPool, + sizeof(ACCESS_RANGE) * HwInitializationData->NumberOfAccessRanges + ); + + if (configInfo.AccessRanges == NULL) { + return ENOMEM; + } + + if (HwInitializationData->HwFindAdapter( + deviceExtension->HwDeviceExtension, // DeviceExtension + HwContext, // HwContext + NULL, // BusInformation + NULL, // ArgumentString + &configInfo, // ConfigurationInformation + &callAgain // Again + ) != SP_RETURN_FOUND) { + + return foundOne ? ESUCCESS : EIO; + } + + ScsiDebugPrint(1,"ScsiPortInitialize: SCSI adapter IRQ is %d\n", + configInfo.BusInterruptLevel); + + ScsiDebugPrint(1,"ScsiPortInitialize: SCSI adapter ID is %d\n", + configInfo.InitiatorBusId[0]); + + deviceExtension->NumberOfBuses = configInfo.NumberOfBuses; + + // + // Free the pointer to the bus data at map register base. This was + // allocated by ScsiPortGetBusData. + // + + if (deviceExtension->MapRegisterBase != NULL) { + ExFreePool(deviceExtension->MapRegisterBase); + } + + // + // Allocate memory for the non cached extension if it has not already been + // allocated. + // + + if (deviceExtension->SrbExtensionSize != 0 && + deviceExtension->SrbExtensionZonePool == NULL) { + + status = SpGetCommonBuffer(deviceExtension, 0); + + if (status != ESUCCESS) { + + return(status); + } + } + + // + // Get the adapter object for this card. + // + + if ((configInfo.Master || configInfo.DmaChannel != 0xFFFFFFFF)) { + + ScsiDebugPrint(1,"ScsiPortInitialize: configInfo.Master\n"); + + deviceDescription.Version = DEVICE_DESCRIPTION_VERSION; + deviceDescription.DmaChannel = configInfo.DmaChannel; + deviceDescription.InterfaceType = configInfo.AdapterInterfaceType; + deviceDescription.BusNumber = configInfo.SystemIoBusNumber; + deviceDescription.DmaWidth = configInfo.DmaWidth; + deviceDescription.DmaSpeed = configInfo.DmaSpeed; + deviceDescription.DmaPort = configInfo.DmaPort; + deviceDescription.MaximumLength = configInfo.MaximumTransferLength; + deviceDescription.ScatterGather = configInfo.ScatterGather; + deviceDescription.Master = configInfo.Master; + deviceDescription.AutoInitialize = FALSE; + deviceDescription.DemandMode = FALSE; + + // BugBug: Make the 0x11000 a define when there is one. + if (configInfo.MaximumTransferLength > 0x11000) { + + deviceDescription.MaximumLength = 0x11000; + + } else { + + deviceDescription.MaximumLength = configInfo.MaximumTransferLength; + + } + + deviceExtension->DmaAdapterObject = HalGetAdapter( + &deviceDescription, + &numberOfPageBreaks + ); + + // + // Set maximum number of page breaks. + // + + if (numberOfPageBreaks > configInfo.NumberOfPhysicalBreaks) { + capabilities->MaximumPhysicalPages = configInfo.NumberOfPhysicalBreaks; + } else { + capabilities->MaximumPhysicalPages = numberOfPageBreaks; + } + + } + + capabilities->Length = sizeof(IO_SCSI_CAPABILITIES); + capabilities->MaximumTransferLength = configInfo.MaximumTransferLength; + ScsiDebugPrint(1, "Maximum physical page breaks = %d. Maximum transfer length = %x\n", capabilities->MaximumPhysicalPages, capabilities->MaximumTransferLength); + + if (HwInitializationData->ReceiveEvent) { + capabilities->SupportedAsynchronousEvents |= + SRBEV_SCSI_ASYNC_NOTIFICATION; + } + + capabilities->TaggedQueuing = HwInitializationData->TaggedQueuing; + capabilities->AdapterScansDown = configInfo.AdapterScansDown; + + // + // Make sure maximum nuber of pages is set to a reasonable value. + // This occurs for mini-ports with no Dma adapter. + // + + if (capabilities->MaximumPhysicalPages == 0) { + + capabilities->MaximumPhysicalPages = + ROUND_TO_PAGES(capabilities->MaximumTransferLength) + 1; + + // + // Honor any limit requested by the mini-port. + // + + if (configInfo.NumberOfPhysicalBreaks < capabilities->MaximumPhysicalPages) { + + capabilities->MaximumPhysicalPages = + configInfo.NumberOfPhysicalBreaks; + } + } + + + if (deviceExtension->DmaAdapterObject != NULL && + !HwInitializationData->NeedPhysicalAddresses) { + + ScsiDebugPrint(1,"ScsiPortInitialize: DmaAdapterObject != NULL \n"); + // + // Allocate the adapter object. For the port driver the adapter object + // and map registers are permentently allocated and used shared between + // all logical units. The adapter is allocated by initializing an event, + // calling IoAllocateAdapterChannel and waiting on the event. When the + // adapter and map registers are available, ScsiPortAllocationRoutine is + // called which set the event. In reality, all this takes zero time since + // the stuff is available immediately. + // + // Allocate the AdapterObject. The number of registers is equal to the + // maximum transfer length supported by the adapter + 1. This insures + // that there will always be a sufficient number of registers. + // + /* TODO: Fix this for the case when there is no maximum transfer length. */ + + IoAllocateAdapterChannel( + deviceExtension->DmaAdapterObject, + DeviceObject, +// configInfo.NumberOfPhysicalBreaks, + capabilities->MaximumPhysicalPages, + ScsiPortAllocationRoutine, + &allocateAdapterEvent + ); + + // + // Wait for adapter object. + // + + ASSERT(deviceExtension->MapRegisterBase); + + deviceExtension->MasterWithAdapter = FALSE; + + } else if (deviceExtension->DmaAdapterObject != NULL) { + + ScsiDebugPrint(1,"ScsiPortInitialize: MasterWithAdapter\n"); + // + // This SCSI adapter is a master with an adapter so a scatter/gather + // list needs to be allocated for each transfer. + // + + deviceExtension->MasterWithAdapter = TRUE; + + + } else { + + ScsiDebugPrint(1,"ScsiPortInitialize: NO MasterWithAdapter\n"); + deviceExtension->MasterWithAdapter = FALSE; + + } // end if (deviceExtension->DmaAdapterObject != NULL) + + // + // Call the hardware dependent driver to do its initialization. + // + + if (!KeSynchronizeExecution( + deviceExtension->InterruptObject, + deviceExtension->HwInitialize, + deviceExtension->HwDeviceExtension + )) { + + ScsiDebugPrint(1,"ScsiPortInitialize: initialization failed\n"); + + return ENODEV; + } + + // + // Allocate properly aligned INQUIRY buffer. + // + + InquiryDataBuffer = ExAllocatePool(NonPagedPool, INQUIRYDATABUFFERSIZE); + + if (InquiryDataBuffer == NULL) { + return(ENOMEM); + } + + // + // Reset the scsi bus. + // + + if (!deviceExtension->HwReset( + deviceExtension->HwDeviceExtension, + 0)){ + + ScsiDebugPrint(1,"Reset SCSI bus failed\n"); + } + + // + // Call the interupt handler for a few microseconds to clear any reset + // interrupts. + // + + for (j = 0; j < 1000 ; j++) { + + FwStallExecution(10); + deviceExtension->HwInterrupt(deviceExtension->HwDeviceExtension); + + } + + // + // Wait 2 seconds for the devices to recover after the reset. + // + +#ifdef DUO + FwStallExecution(2 * 1000 * 100); +#else + FwStallExecution(2 * 1000 * 50); +#endif + + // + // Find devices on each SCSI bus. + // + + // + // Allocate buffer for SCSI bus scan information. + // + + deviceExtension->ScsiInfo = ExAllocatePool(NonPagedPool, + deviceExtension->NumberOfBuses * sizeof(PSCSI_BUS_SCAN_DATA) + + 4); + + if (deviceExtension->ScsiInfo) { + + deviceExtension->ScsiInfo->NumberOfBuses = deviceExtension->NumberOfBuses; + + // + // Find devices on each SCSI bus. + // + + for (scsiBus = 0; scsiBus < deviceExtension->NumberOfBuses; scsiBus++) { + deviceExtension->ScsiInfo->BusScanData[scsiBus] = + ScsiBusScan(deviceExtension, + scsiBus, + configInfo.InitiatorBusId[scsiBus]); + } + } + + // + // Save the device object for use by the driver. + // + + ScsiPortDeviceObject[*scsiPortNumber] = DeviceObject; + + // + // Bump SCSI host bus adapters count. + // + + (*scsiPortNumber)++; + + foundOne = TRUE; + + // + // If the adapter wants to be called again with the same configuration data + // then start over from the begining again. + // + + if (callAgain) { + goto CallAgain; + + } + + return ESUCCESS; + +} // end ScsiPortInitialize() + +IO_ALLOCATION_ACTION +ScsiPortAllocationRoutine ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID MapRegisterBase, + IN PVOID Context + ) + +/*++ + +Routine Description: + + This function is called by IoAllocateAdapterChannel when sufficent resources + are available to the driver. This routine saves the MapRegisterBase in the + device object and set the event pointed to by the context parameter. + +Arguments: + + DeviceObject - Pointer to the device object to which the adapter is being + allocated. + + Irp - Unused. + + MapRegisterBase - Supplied by the Io subsystem for use in IoMapTransfer. + + Context - Supplies a pointer to an event which is set to indicate the + AdapterObject has been allocated. + +Return Value: + + KeepObject - Indicates the adapter and mapregisters should remain allocated + after return. + +--*/ + +{ + ((PDEVICE_EXTENSION) DeviceObject->DeviceExtension)->MapRegisterBase = + MapRegisterBase; + + UNREFERENCED_PARAMETER(Irp); + UNREFERENCED_PARAMETER(Context); + + return(KeepObject); +} + +IO_ALLOCATION_ACTION +SpBuildScatterGather( + IN struct _DEVICE_OBJECT *DeviceObject, + IN struct _IRP *Irp, + IN PVOID MapRegisterBase, + IN PVOID Context + ) + +/*++ + +Routine Description: + + This function is called by the I/O system when an adapter object and map + registers have been allocated. This routine then builds a scatter/gather + list for use by the mini-port driver. Next it sets the timeout and + the current Irp for the logical unit. Finally it calls the mini-port + StartIo routine. Once that routines complete, this routine will return + requesting that the adapter be freed and but the registers remain allocated. + The registers will be freed the request completes. + +Arguments: + + DeviceObject - Supplies a pointer to the port driver device object. + + Irp - Supplies a pointer to the current Irp. + + MapRegisterBase - Supplies a context pointer to be used with calls the + adapter object routines. + + Context - Supplies a pointer to the logical unit structure. + +Return Value: + + Returns DeallocateObjectKeepRegisters so that the adapter object can be + used by other logical units. + +--*/ + +{ + BOOLEAN writeToDevice; + PIO_STACK_LOCATION irpstack = IoGetCurrentIrpStackLocation(Irp); + PLOGICAL_UNIT_EXTENSION logicalUnit; + PSCSI_REQUEST_BLOCK srb; + PSRB_SCATTER_GATHER scatterList; + ULONG totalLength; + + ScsiDebugPrint(1,"ScsiPortBuildSscaterGather: Enter routine\n"); + + logicalUnit = Context; + srb = (PSCSI_REQUEST_BLOCK)irpstack->Parameters.Others.Argument1; + scatterList = logicalUnit->ScatterGather; + totalLength = 0; + + // + // Save the MapRegisterBase for later use to deallocate the map registers. + // + + logicalUnit->MapRegisterBase = MapRegisterBase; + + // + // Build the scatter/gather list by looping throught the transfer calling + // I/O map transfer. + // + + writeToDevice = srb->SrbFlags & SRB_FLAGS_DATA_OUT ? TRUE : FALSE; + + while (totalLength < srb->DataTransferLength) { + + // + // Request that the rest of the transfer be mapped. + // + + scatterList->Length = srb->DataTransferLength - totalLength; + + // + // Since we are a master call I/O map transfer with a NULL adapter. + // + + scatterList->PhysicalAddress = IoMapTransfer( + NULL, + Irp->MdlAddress, + MapRegisterBase, + (PCCHAR) srb->DataBuffer + totalLength, + &scatterList->Length, + writeToDevice + ).LowPart; + + totalLength += scatterList->Length; + scatterList++; + } + + // + // Set request timeout value from Srb SCSI extension in Irp. + // + + logicalUnit->RequestTimeoutCounter = srb->TimeOutValue; + + // + // Set current request for this logical unit. + // + + logicalUnit->CurrentRequest = Irp; + + /* TODO: Check the return value. */ + KeSynchronizeExecution( + ((PDEVICE_EXTENSION) DeviceObject->DeviceExtension)->InterruptObject, + SpStartIoSynchronized, + DeviceObject + ); + + return(DeallocateObjectKeepRegisters); + +} + +VOID +ScsiPortExecute( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine calls the start I/O routine an waits for the request to + complete. During the wait for complete the interrupt routine is called, + also the timer routines are called at the appropriate times. After the + request completes a check is made to determine if an request sense needs + to be issued. + +Arguments: + + DeviceObject - Supplies pointer to Adapter device object. + + Irp - Supplies a pointer to an IRP. + +Return Value: + + Nothing. + +--*/ + +{ + ULONG milliSecondTime; + ULONG secondTime; + ULONG completionDelay; + PDEVICE_EXTENSION deviceExtension; + PIO_STACK_LOCATION irpstack = IoGetCurrentIrpStackLocation(Irp); + PSCSI_REQUEST_BLOCK srb = (PSCSI_REQUEST_BLOCK)irpstack->Parameters.Others.Argument1; + PVOID logicalUnit; + + ScsiDebugPrint(1,"ScsiPortExecute: Enter routine\n"); + + deviceExtension = DeviceObject->DeviceExtension; + logicalUnit = GetLogicalUnitExtension(deviceExtension, srb->TargetId); + + if (logicalUnit == NULL) { + Irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST; + return; + } + + // + // Mark IRP as pending. + // + + Irp->PendingReturned = TRUE; + + // + // Start the request. + // + + ScsiPortStartIo( DeviceObject, Irp); + + // + // The completion delay controls how long interrupts are serviced after + // a request has been completed. This allows interrupts which occur after + // a completion to be serviced. + // + + completionDelay = COMPLETION_DELAY; + + // + // Wait for the IRP to complete. + // + + while (Irp->PendingReturned && completionDelay) { + + // + // Wait 1 second then call the scsi port timer routine. + // + + for (secondTime = 0; secondTime < 2; secondTime++) { + + for (milliSecondTime = 0; milliSecondTime < (250 * 1000 / PD_INTERLOOP_STALL); milliSecondTime++) { + + ScsiPortInterrupt(NULL, DeviceObject); + + if (!Irp->PendingReturned) { + if (completionDelay-- == 0) { + goto done; + } + } + + if (deviceExtension->Flags & PD_ENABLE_CALL_REQUEST) { + + // + // Call the mini-port requested routine. + // + + deviceExtension->Flags &= ~PD_ENABLE_CALL_REQUEST; + deviceExtension->HwRequestInterrupt(deviceExtension->HwDeviceExtension); + + if (deviceExtension->Flags & PD_DISABLE_CALL_REQUEST) { + + deviceExtension->Flags &= ~(PD_DISABLE_INTERRUPTS | PD_DISABLE_CALL_REQUEST); + deviceExtension->HwRequestInterrupt(deviceExtension->HwDeviceExtension); + + } + } + + FwStallExecution(PD_INTERLOOP_STALL); + + // + // Check the miniport timer. + // + + if (deviceExtension->TimerValue != 0) { + + deviceExtension->TimerValue--; + + if (deviceExtension->TimerValue == 0) { + + // + // The timer timed out so called requested timer routine. + // + + deviceExtension->HwTimerRequest(deviceExtension->HwDeviceExtension); + } + } + } + + } + + ScsiPortTickHandler(DeviceObject, NULL); + } + +done: + + if (!NT_SUCCESS(Irp->IoStatus.Status)) { + PIO_STACK_LOCATION irpstack = IoGetCurrentIrpStackLocation(Irp); + PSCSI_REQUEST_BLOCK Srb = (PSCSI_REQUEST_BLOCK)irpstack->Parameters.Others.Argument1; + + // + // Determine if a REQUEST SENSE command needs to be done. + // Check that a CHECK_CONDITION was received, an autosense has not + // been done already, and that autosense has been requested. + // + + if (srb->ScsiStatus == SCSISTAT_CHECK_CONDITION && + !(srb->SrbStatus & SRB_STATUS_AUTOSENSE_VALID) && + srb->SenseInfoBuffer) { + + // + // Call IssueRequestSense and it will complete the request after + // the REQUEST SENSE completes. + // + + IssueRequestSense(deviceExtension, Srb); + } + } +} + +VOID +ScsiPortStartIo ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + +Arguments: + + DeviceObject - Supplies pointer to Adapter device object. + Irp - Supplies a pointer to an IRP. + +Return Value: + + Nothing. + +--*/ + +{ + PIO_STACK_LOCATION irpstack = IoGetCurrentIrpStackLocation(Irp); + PSCSI_REQUEST_BLOCK Srb = (PSCSI_REQUEST_BLOCK)irpstack->Parameters.Others.Argument1; + PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension; + PLOGICAL_UNIT_EXTENSION logicalUnit; + PFULL_SCSI_REQUEST_BLOCK FullSrb; + NTSTATUS status; + + ScsiDebugPrint(1,"ScsiPortStartIo: Enter routine\n"); + + FullSrb = CONTAINING_RECORD(Srb, FULL_SCSI_REQUEST_BLOCK, Srb); + + if (deviceExtension->SrbExtensionZonePool && (Srb->SrbExtension == NULL + || deviceExtension->SrbExtensionSize > FullSrb->SrbExtensionSize)) { + + ScsiDebugPrint(1,"ScsiPortStartIo: Allocate SRB\n"); + + // + // Allocate SRB extension from zone. + // + + Srb->SrbExtension = deviceExtension->SrbExtensionPointer; + + (PCCHAR) deviceExtension->SrbExtensionPointer += + deviceExtension->SrbExtensionSize; + + FullSrb->SrbExtensionSize = deviceExtension->SrbExtensionSize; + + if ((ULONG) deviceExtension->SrbExtensionPointer > + (ULONG) deviceExtension->NonCachedExtension) { + ScsiDebugPrint(0, "NtLdr: ScsiPortStartIo: Srb extension overflow. Too many srb extension allocated.\n"); + } + + ScsiDebugPrint(2,"ExInterlockedAllocateFromZone: %lx\n", + Srb->SrbExtension); + + ScsiDebugPrint(2,"Srb %lx\n",Srb); + + + } + + // + // Get logical unit extension. + // + + logicalUnit = GetLogicalUnitExtension(deviceExtension, Srb->TargetId); + + // + // Flush the data buffer if necessary. + // + + if (Srb->SrbFlags & (SRB_FLAGS_DATA_IN | SRB_FLAGS_DATA_OUT)) { + + ScsiDebugPrint(1,"ScsiPortStartIo: SRB_FLAGS_DATA_IN | OUT\n"); + + if (Srb->DataTransferLength > deviceExtension->Capabilities.MaximumTransferLength) { + + ScsiDebugPrint(1, "Scsiboot: ScsiPortStartIo Length Exceeds limit %x, %x\n Hit any key\n", + Srb->DataTransferLength, + deviceExtension->Capabilities.MaximumTransferLength + ); + + } + + KeFlushIoBuffers( + Irp->MdlAddress, + Srb->SrbFlags & SRB_FLAGS_DATA_IN ? TRUE : FALSE, + TRUE + ); + + // + // Determine if this adapter needs map registers + // + + if (deviceExtension->MasterWithAdapter) { + + + ScsiDebugPrint(1,"ScsiPortStartIo: MasterWithAdapter\n"); + // + // Calculate the number of map registers needed for this transfer. + // + + logicalUnit->NumberOfMapRegisters = ADDRESS_AND_SIZE_TO_SPAN_PAGES( + Srb->DataBuffer, + Srb->DataTransferLength + ); + + // + // Allocate the adapter channel with sufficient map registers + // for the transfer. + // + + status = IoAllocateAdapterChannel( + deviceExtension->DmaAdapterObject, // AdapterObject + DeviceObject, // DeviceObject. + logicalUnit->NumberOfMapRegisters, // NumberOfMapRegisters + SpBuildScatterGather, // ExecutionRoutine + logicalUnit // Context + ); + + if (!NT_SUCCESS(status)) { + + ScsiDebugPrint(1,"ScsiPortStartIo: IoAllocateAdapterChannel FAILED\n"); + ; + } + + // + // The execution routine called by IoAllocateChannel will do the + // rest of the work so just return. + // + + ScsiDebugPrint(1,"ScsiPortStartIo: IoAllocateAdapterChannel SUCCES\n"); + return; + } + } + + // + // Set request timeout value from Srb SCSI extension in Irp. + // + + logicalUnit->RequestTimeoutCounter = Srb->TimeOutValue; + + // + // Set current request for this logical unit. + // + + logicalUnit->CurrentRequest = Irp; + + ScsiDebugPrint(1,"ScsiPortStartIo: Calling KeSynchronizeExecution\n"); + + /* TODO: Check the return value. */ + KeSynchronizeExecution( + deviceExtension->InterruptObject, + SpStartIoSynchronized, + DeviceObject + ); + + return; + +} // end ScsiPortStartIO() + + +BOOLEAN +SpStartIoSynchronized ( + PVOID ServiceContext + ) + +/*++ + +Routine Description: + + This routine calls the dependent driver start io routine. + +Arguments: + + ServiceContext - Supplies the pointer to the device object. + +Return Value: + + Returns the value returned by the dependent start I/O routine. + + +--*/ + +{ + PDEVICE_OBJECT DeviceObject = ServiceContext; + PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension; + PIO_STACK_LOCATION irpstack; + PSCSI_REQUEST_BLOCK Srb; + + ScsiDebugPrint(1,"ScsiPortStartIoSynchronized: Enter routine\n"); + + irpstack = IoGetCurrentIrpStackLocation(DeviceObject->CurrentIrp); + Srb = (PSCSI_REQUEST_BLOCK)irpstack->Parameters.Others.Argument1; + + ScsiDebugPrint(3, "SpPortStartIoSynchronized: SRB %lx\n", + Srb); + + ScsiDebugPrint(3, "SpPortStartIoSynchronized: IRP %lx\n", + DeviceObject->CurrentIrp); + + // + // Disable all synchronous transfers. + // + + Srb->SrbFlags |= + (SRB_FLAGS_DISABLE_SYNCH_TRANSFER | SRB_FLAGS_DISABLE_DISCONNECT | SRB_FLAGS_DISABLE_AUTOSENSE); + + return deviceExtension->HwStartIo( + deviceExtension->HwDeviceExtension, + Srb + ); + +} // end SpStartIoSynchronized() + + +BOOLEAN +ScsiPortInterrupt( + IN PKINTERRUPT Interrupt, + IN PDEVICE_OBJECT DeviceObject + ) + +/*++ + +Routine Description: + + +Arguments: + + Interrupt + + Device Object + +Return Value: + + Returns TRUE if interrupt expected. + +--*/ + +{ + PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension; + + UNREFERENCED_PARAMETER(Interrupt); + + if (deviceExtension->HwInterrupt(deviceExtension->HwDeviceExtension)) { + + return TRUE; + + } else { + + return FALSE; + } + +} // end ScsiPortInterrupt() + + +VOID +ScsiPortCompletionDpc( + IN PKDPC Dpc, + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context + ) + +/*++ + +Routine Description: + +Arguments: + + Dpc + DeviceObject + Irp - not used + Context - not used + +Return Value: + + None. + +--*/ + +{ + PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension; + PIO_STACK_LOCATION irpstack; + PSCSI_REQUEST_BLOCK Srb; + PLOGICAL_UNIT_EXTENSION luExtension; + + UNREFERENCED_PARAMETER(Dpc); + UNREFERENCED_PARAMETER(Context); + + ScsiDebugPrint(1, "ScsiPortCompletionDpc Entered\n"); + + // + // Acquire the spinlock to protect the flags structure and the saved + // interrupt context. + // + + KeAcquireSpinLock(&deviceExtension->SpinLock, ¤tIrql); + + // + // Check for a flush DMA adapter object request. + // + + if (deviceExtension->InterruptFlags & PD_FLUSH_ADAPTER_BUFFERS) { + + // + // Call IoFlushAdapterBuffers using the parameters saved from the last + // IoMapTransfer call. + // + + IoFlushAdapterBuffers( + deviceExtension->DmaAdapterObject, + ((PIRP)deviceExtension->FlushAdapterParameters.Srb->OriginalRequest) + ->MdlAddress, + deviceExtension->MapRegisterBase, + deviceExtension->FlushAdapterParameters.LogicalAddress, + deviceExtension->FlushAdapterParameters.Length, + (BOOLEAN)(deviceExtension->FlushAdapterParameters.Srb->SrbFlags + & SRB_FLAGS_DATA_OUT ? TRUE : FALSE) + ); + + } + + // + // Check for an IoMapTransfer DMA request. + // + + if (deviceExtension->InterruptFlags & PD_MAP_TRANSFER) { + + // + // Call IoMapTransfer using the parameters saved from the + // interrupt level. + // + + IoMapTransfer( + deviceExtension->DmaAdapterObject, + ((PIRP)deviceExtension->MapTransferParameters.Srb->OriginalRequest) + ->MdlAddress, + deviceExtension->MapRegisterBase, + deviceExtension->MapTransferParameters.LogicalAddress, + &deviceExtension->MapTransferParameters.Length, + (BOOLEAN)(deviceExtension->MapTransferParameters.Srb->SrbFlags + & SRB_FLAGS_DATA_OUT ? TRUE : FALSE) + ); + + // + // Save the paramters for IoFlushAdapterBuffers. + // + + deviceExtension->FlushAdapterParameters = + deviceExtension->MapTransferParameters; + + // + // If necessary notify the mini-port driver that the DMA has been + // started. + // + + if (deviceExtension->HwDmaStarted) { + KeSynchronizeExecution( + &deviceExtension->InterruptObject, + (PKSYNCHRONIZE_ROUTINE) deviceExtension->HwDmaStarted, + deviceExtension->HwDeviceExtension + ); + } + + } + + // + // Process any completed requests. + // + + while (deviceExtension->CompletedRequests != NULL) { + + Irp = deviceExtension->CompletedRequests; + irpstack = IoGetCurrentIrpStackLocation(Irp); + Srb = (PSCSI_REQUEST_BLOCK)irpstack->Parameters.Others.Argument1; + luExtension = + GetLogicalUnitExtension(deviceExtension, Srb->TargetId); + + ScsiDebugPrint(1, "ScsiPortCompletionDpc: SRB %lx\n", Srb); + ScsiDebugPrint(1, "ScsiPortCompletionDpc: IRP %lx\n", Irp); + + // + // Remove the request from the linked-list. + // + + deviceExtension->CompletedRequests = + irpstack->Parameters.Others.Argument3; + + // + // Reset request timeout counter. + // + + luExtension->RequestTimeoutCounter = -1; + + // + // Flush the adapter buffers if necessary. + // + + if (luExtension->MapRegisterBase) { + + // + // Since we are a master call I/O flush adapter buffers with a NULL + // adapter. + // + + IoFlushAdapterBuffers( + deviceExtension->DmaAdapterObject, + Irp->MdlAddress, + luExtension->MapRegisterBase, + Srb->DataBuffer, + Srb->DataTransferLength, + (BOOLEAN) (Srb->SrbFlags & SRB_FLAGS_DATA_OUT ? TRUE : FALSE) + ); + + // + // Free the map registers. + // + + IoFreeMapRegisters( + deviceExtension->DmaAdapterObject, + luExtension->MapRegisterBase, + luExtension->NumberOfMapRegisters + ); + + // + // Clear the MapRegisterBase. + // + + luExtension->MapRegisterBase = NULL; + + } + + // + // Set IRP status. Class drivers will reset IRP status based + // on request sense if error. + // + + if (SRB_STATUS(Srb->SrbStatus) == SRB_STATUS_SUCCESS) { + Irp->IoStatus.Status = STATUS_SUCCESS; + } else { + Irp->IoStatus.Status = STATUS_UNSUCCESSFUL; + } + + // + // Move bytes transfered to IRP. + // + + Irp->IoStatus.Information = Srb->DataTransferLength; + + // + // If success then start next packet. + // Not starting packet effectively + // freezes the queue. + // + + if (SRB_STATUS(Srb->SrbStatus) == SRB_STATUS_SUCCESS) { + + ScsiDebugPrint( + 1, + "ScsiPortCompletionDpc: Iocompletion IRP %lx\n", + Irp + ); + + IoCompleteRequest(Irp, 2); + + } else { + + if ( Srb->ScsiStatus == SCSISTAT_BUSY && + (luExtension->RetryCount++ < 3)) { + // + // If busy status is returned, then indicate that the logical + // unit is busy. The timeout code will restart the request + // when it fires. Reset the status to pending. + // + Srb->SrbStatus = SRB_STATUS_PENDING; + luExtension->CurrentRequest = Irp; + luExtension->Flags |= PD_LOGICAL_UNIT_IS_BUSY; + } else { + + + ScsiDebugPrint( + 2, + "ScsiPortCompletionDpc: Iocompletion IRP %lx\n", + Irp + ); + + IoCompleteRequest(Irp, 2); + } + } + } + + // + // Release the spinlock. + // + + // KeReleaseSpinLock(&deviceExtension->SpinLock, currentIrql); + + return; + +} // end ScsiPortCompletionDpc() + + +ARC_STATUS +IssueInquiry( + IN PDEVICE_EXTENSION DeviceExtension, + IN PLUNINFO LunInfo + ) + +/*++ + +Routine Description: + + Build IRP, SRB and CDB for SCSI INQUIRY command. + +Arguments: + + DeviceExtension - address of adapter's device object extension. + LunInfo - address of buffer for INQUIRY information. + +Return Value: + + ARC_STATUS + +--*/ + +{ + PIRP irp; + PIO_STACK_LOCATION irpstack; + PCDB cdb; + PSCSI_REQUEST_BLOCK srb; + ARC_STATUS status; + ULONG retryCount = 0; + + ScsiDebugPrint(1,"IssueInquiry: Enter routine\n"); + + if (InquiryDataBuffer == NULL) { + return ENOMEM; + } + +inquiryRetry: + + // + // Build IRP for this request. + // + + irp = InitializeIrp( + &PrimarySrb, + IRP_MJ_SCSI, + DeviceExtension->DeviceObject, + (PVOID)InquiryDataBuffer, + INQUIRYDATABUFFERSIZE + ); + + irpstack = IoGetNextIrpStackLocation(irp); + + // + // Set major and minor codes. + // + + irpstack->MajorFunction = IRP_MJ_SCSI; + + // + // Fill in SRB fields. + // + + irpstack->Parameters.Others.Argument1 = &PrimarySrb; + srb = &PrimarySrb.Srb; + + srb->Length = sizeof(SCSI_REQUEST_BLOCK); + srb->PathId = LunInfo->PathId; + srb->TargetId = LunInfo->TargetId; + srb->Lun = LunInfo->Lun; + + srb->Function = SRB_FUNCTION_EXECUTE_SCSI; + + srb->SrbFlags = SRB_FLAGS_DATA_IN | SRB_FLAGS_DISABLE_SYNCH_TRANSFER | SRB_FLAGS_DISABLE_DISCONNECT; + + srb->SrbStatus = srb->ScsiStatus = 0; + + srb->OriginalRequest = irp; + + srb->NextSrb = 0; + + // + // Set timeout to 5 seconds. + // + + srb->TimeOutValue = 5; + + srb->CdbLength = 6; + + srb->SenseInfoBufferLength = 0; + srb->SenseInfoBuffer = 0; + + srb->DataBuffer = MmGetMdlVirtualAddress(irp->MdlAddress); + srb->DataTransferLength = INQUIRYDATABUFFERSIZE; + + cdb = (PCDB)srb->Cdb; + + // + // Set CDB operation code. + // + + cdb->CDB6INQUIRY.OperationCode = SCSIOP_INQUIRY; + + // + // Set CDB LUN. + // + + cdb->CDB6INQUIRY.LogicalUnitNumber = LunInfo->Lun; + cdb->CDB6INQUIRY.Reserved1 = 0; + + // + // Set allocation length to inquiry data buffer size. + // + + cdb->CDB6INQUIRY.AllocationLength = INQUIRYDATABUFFERSIZE; + + // + // Zero reserve field and + // Set EVPD Page Code to zero. + // Set Control field to zero. + // (See SCSI-II Specification.) + // + + cdb->CDB6INQUIRY.PageCode = 0; + cdb->CDB6INQUIRY.IReserved = 0; + cdb->CDB6INQUIRY.Control = 0; + + // + // Call port driver to handle this request. + // + + (VOID)IoCallDriver(DeviceExtension->DeviceObject, irp); + + + if (SRB_STATUS(srb->SrbStatus) != SRB_STATUS_SUCCESS) { + + ScsiDebugPrint(2,"IssueInquiry: Inquiry failed SRB status %x\n", + srb->SrbStatus); + + // + // NOTE: if INQUIRY fails with a data underrun, + // indicate success and let the class drivers + // determine whether the inquiry information + // is useful. + // + + if (SRB_STATUS(srb->SrbStatus) == SRB_STATUS_DATA_OVERRUN) { + + // + // Copy INQUIRY buffer to LUNINFO. + // + + ScsiDebugPrint(1,"IssueInquiry: Data underrun at TID %d\n", + LunInfo->TargetId); + + RtlMoveMemory(LunInfo->InquiryData, + InquiryDataBuffer, + INQUIRYDATABUFFERSIZE); + + status = STATUS_SUCCESS; + + } else if ((SRB_STATUS(srb->SrbStatus) != SRB_STATUS_SELECTION_TIMEOUT) && (retryCount++ < 2)) { + + // + // If the selection did not time out then retry the request. + // + + ScsiDebugPrint(2,"IssueInquiry: Retry %d\n", retryCount); + goto inquiryRetry; + + } else { + + status = EIO; + + } + + + } else { + + // + // Copy INQUIRY buffer to LUNINFO. + // + + RtlMoveMemory(LunInfo->InquiryData, + InquiryDataBuffer, + INQUIRYDATABUFFERSIZE); + + status = STATUS_SUCCESS; + } + + return status; + +} // end IssueInquiry() + + +PSCSI_BUS_SCAN_DATA +ScsiBusScan( + IN PDEVICE_EXTENSION DeviceExtension, + IN UCHAR ScsiBus, + IN UCHAR InitiatorBusId + ) + +/*++ + +Routine Description: + +Arguments: + + DeviceExtension + ScsiBus + +Return Value: + + SCSI configuration information + + +--*/ +{ + PSCSI_BUS_SCAN_DATA busScanData; + PLUNINFO lunInfo; + UCHAR target; + UCHAR device = 0; + PLOGICAL_UNIT_EXTENSION nextLogicalUnitExtension; + + + ScsiDebugPrint(1,"ScsiBusScan: Enter routine\n"); + + busScanData = ExAllocatePool(NonPagedPool, + sizeof(SCSI_BUS_SCAN_DATA)); + + if (busScanData == NULL) { + + // + // Insufficient system resources to complete bus scan. + // + + return NULL; + } + + RtlZeroMemory(busScanData,sizeof(SCSI_BUS_SCAN_DATA)); + + busScanData->Length = sizeof(SCSI_CONFIGURATION_INFO); + + // + // Create first LUNINFO. + // + + lunInfo = ExAllocatePool(NonPagedPool, sizeof(LUNINFO)); + + if (lunInfo == NULL) { + + // + // Insufficient system resources to complete bus scan. + // + + return NULL; + } + + RtlZeroMemory(lunInfo, sizeof(LUNINFO)); + + // + // Create first logical unit extension. + // + + nextLogicalUnitExtension = CreateLogicalUnitExtension(DeviceExtension); + + if (nextLogicalUnitExtension == NULL) { + return(NULL); + } + + // + // Link logical unit extension on list. + // + + nextLogicalUnitExtension->NextLogicalUnit = DeviceExtension->LogicalUnitList; + + DeviceExtension->LogicalUnitList = nextLogicalUnitExtension; + + // + // Issue inquiry command to each target id to find devices. + // + // NOTE: Does not handle multiple logical units per target id. + // + + for (target = 8; target > 0; target--) { + + if (InitiatorBusId == target-1) { + continue; + } + + nextLogicalUnitExtension->PathId = lunInfo->PathId = ScsiBus; + + nextLogicalUnitExtension->TargetId = lunInfo->TargetId = target-1; + + nextLogicalUnitExtension->Lun = lunInfo->Lun = 0; + + // + // Rezero hardware logigal unit extension if it's being recycled. + // + + if (DeviceExtension->HwLogicalUnitExtensionSize) { + + if (nextLogicalUnitExtension->SpecificLuExtension) { + + RtlZeroMemory(nextLogicalUnitExtension->SpecificLuExtension, + DeviceExtension->HwLogicalUnitExtensionSize); + } + + } + + // + // Issue inquiry command. + // + + ScsiDebugPrint(3,"ScsiBusScan: LunInfo at %lx\n", lunInfo); + + ScsiDebugPrint(3, "ScsiBusScan: Device extension %lx\n", + DeviceExtension); + + ScsiDebugPrint(2,"ScsiBusScan: Try TargetId %d LUN 0\n", target-1); + + if (IssueInquiry(DeviceExtension, lunInfo) == ESUCCESS) { + + ScsiDebugPrint(1,"ScsiBusScan: Found Device %d", device); + ScsiDebugPrint(1," at Target Id %d", lunInfo->TargetId); + ScsiDebugPrint(1," LUN %d\n", lunInfo->Lun); + // + // Link LUN information on list. + // + + // lunInfo->DeviceObject = DeviceExtension->DeviceObject; + + lunInfo->NextLunInfo = busScanData->LunInfoList; + busScanData->LunInfoList = lunInfo; + + // + // This buffer is used. Get another. + // + + lunInfo = ExAllocatePool(NonPagedPool, sizeof(LUNINFO)); + + if (lunInfo == NULL) { + + // + // Insufficient system resources to complete bus scan. + // + + return busScanData; + } + + RtlZeroMemory(lunInfo, sizeof(LUNINFO)); + + // + // Current logical unit extension claimed. + // Create next logical unit. + // + + nextLogicalUnitExtension = + CreateLogicalUnitExtension(DeviceExtension); + + if (nextLogicalUnitExtension == NULL) { + return busScanData; + } + + // + // Link logical unit extension on list. + // + + nextLogicalUnitExtension->NextLogicalUnit = + DeviceExtension->LogicalUnitList; + + DeviceExtension->LogicalUnitList = nextLogicalUnitExtension; + + device++; + } + + } // end for (target ... + + // + // Remove unused logicalunit extension from list. + // + + DeviceExtension->LogicalUnitList = + DeviceExtension->LogicalUnitList->NextLogicalUnit; + + ExFreePool(nextLogicalUnitExtension); + ExFreePool(lunInfo); + + busScanData->NumberOfLogicalUnits = device; + ScsiDebugPrint(1, + "ScsiBusScan: Found %d devices on SCSI bus %d\n", + device, + ScsiBus); + + return busScanData; + +} // end ScsiBusScan() + + +PLOGICAL_UNIT_EXTENSION +CreateLogicalUnitExtension( + IN PDEVICE_EXTENSION DeviceExtension + ) + +/*++ + +Routine Description: + + Create logical unit extension. + +Arguments: + + DeviceExtension + PathId + +Return Value: + + Logical unit extension + + +--*/ +{ + PLOGICAL_UNIT_EXTENSION logicalUnitExtension; + + ScsiDebugPrint(1,"ScsiPortCreateLogicalUnitExtension: Enter routine.\n"); + // + // Create logical unit extension and link in chain. + // + + logicalUnitExtension = + ExAllocatePool(NonPagedPool, sizeof(LOGICAL_UNIT_EXTENSION)); + + if (logicalUnitExtension == NULL) { + return(NULL); + } + + // + // Zero logical unit extension. + // + + RtlZeroMemory(logicalUnitExtension, sizeof(LOGICAL_UNIT_EXTENSION)); + + // + // Allocate miniport driver logical unit extension if necessary. + // + + if (DeviceExtension->HwLogicalUnitExtensionSize) { + + logicalUnitExtension->SpecificLuExtension = + ExAllocatePool(NonPagedPool, + DeviceExtension->HwLogicalUnitExtensionSize); + + if (logicalUnitExtension->SpecificLuExtension == NULL) { + return(NULL); + } + + // + // Zero hardware logical unit extension. + // + + RtlZeroMemory(logicalUnitExtension->SpecificLuExtension, + DeviceExtension->HwLogicalUnitExtensionSize); + } + + // + // Set timer counters in LogicalUnits to -1 to indicate no + // outstanding requests. + // + + logicalUnitExtension->RequestTimeoutCounter = -1; + + // + // Clear the current request field. + // + + logicalUnitExtension->CurrentRequest = NULL; + + return logicalUnitExtension; + +} // end CreateLogicalUnitExtension() + + +// +// Routines providing service to hardware dependent driver. +// + +SCSI_PHYSICAL_ADDRESS +ScsiPortGetPhysicalAddress( + IN PVOID HwDeviceExtension, + IN PSCSI_REQUEST_BLOCK Srb, + IN PVOID VirtualAddress, + OUT ULONG *Length +) + +/*++ + +Routine Description: + +Arguments: + +Return Value: + +--*/ + +{ + PDEVICE_EXTENSION deviceExtension = + ((PDEVICE_EXTENSION) HwDeviceExtension) - 1; + PLOGICAL_UNIT_EXTENSION LogicalUnit; + PSRB_SCATTER_GATHER scatterList; + PIRP irp; + PMDL mdl; + ULONG byteOffset; + ULONG whichPage; + PULONG pages; + ULONG address; + + + if (Srb == NULL) { + + if (deviceExtension->SrbExtensionZonePool) { + + address = (PUCHAR) VirtualAddress - (PUCHAR) deviceExtension->SrbExtensionZonePool + + deviceExtension->PhysicalZoneBase; + + } else { + + address = MmGetPhysicalAddress(VirtualAddress).LowPart; + } + + // + // Return the requested length. + // + + } else if (deviceExtension->MasterWithAdapter) { + + // + // A scatter/gather list has already been allocated use it to determine + // the physical address and length. Get the scatter/gather list. + // + + LogicalUnit = GetLogicalUnitExtension(deviceExtension, Srb->TargetId); + scatterList = LogicalUnit->ScatterGather; + + + + // + // Calculate byte offset into the data buffer. + // + + byteOffset = (PCHAR) VirtualAddress - (PCHAR) Srb->DataBuffer; + + // + // Find the appropirate entry in the scatter/gatter list. + // + ScsiDebugPrint(1,"GetPhysAdr MasterWithAdapter byte offset = %lx\n",byteOffset); + while (byteOffset >= scatterList->Length) { + + byteOffset -= scatterList->Length; + scatterList++; + } + + // + // Calculate the physical address and length to be returned. + // + + *Length = scatterList->Length - byteOffset; + ScsiDebugPrint(1,"GetPhysAdr MasterWithAdapter ScatterList PhysAdr = %lx\n",scatterList->PhysicalAddress); + return(ScsiPortConvertUlongToPhysicalAddress(scatterList->PhysicalAddress + byteOffset)); + + } else { + + // + // Get IRP from SRB. + // + + irp = Srb->OriginalRequest; + + // + // Get MDL from IRP. + // + + mdl = irp->MdlAddress; + + // + // Calculate byte offset from + // beginning of first physical page. + // + + byteOffset = (PCHAR)VirtualAddress - (PCHAR)mdl->StartVa; + + // + // Calculate which physical page. + // + + whichPage = byteOffset >> PAGE_SHIFT; + + // + // Calculate beginning of physical page array. + // + + pages = (PULONG)(mdl + 1); + + // + // Calculate physical address. + // + + address = (pages[whichPage] << PAGE_SHIFT) + + BYTE_OFFSET(VirtualAddress); + + // + // Assume the buffer is contiguous. Just return the requested length. + // + } + + ScsiDebugPrint(1,"ScsiPortGetPhysicalAddress Virtual=%lx Physical=%lx\n",VirtualAddress,address); + + return ScsiPortConvertUlongToPhysicalAddress(address); + +} // end ScsiPortGetPhysicalAddress() + +/*++ + +PVOID +ScsiPortGetVirtualAddress( + IN PVOID HwDeviceExtension, + IN SCSI_PHYSICAL_ADDRESS PhysicalAddress + ) + + +Routine Description: + + This routine is returns a virtual address associated with a + physical address, if the physical address was obtained by a + call to ScsiPortGetPhysicalAddress. + +Arguments: + + PhysicalAddress + +Return Value: + + Virtual address if physical page hashed. + NULL if physical page not found in hash. + + +{ + PDEVICE_EXTENSION deviceExtension = + ((PDEVICE_EXTENSION) HwDeviceExtension) - 1; + PVOID address; + + ScsiDebugPrint(1,"ScsiPortGetVirualAddress: Enter routine.\n"); + + + + address = ScsiPortConvertPhysicalAddressToUlong(PhysicalAddress) + - deviceExtension->PhysicalZoneBase + + (PUCHAR)deviceExtension->SrbExtensionZonePool; + + return address; + +} // end ScsiPortGetVirtualAddress() +--*/ + + +PVOID +ScsiPortGetLogicalUnit( + IN PVOID HwDeviceExtension, + IN UCHAR PathId, + IN UCHAR TargetId, + IN UCHAR Lun + ) + +/*++ + +Routine Description: + + Walk port driver's logical unit extension list searching + for entry. + +Arguments: + + HwDeviceExtension - The port driver's device extension follows + the miniport's device extension and contains a pointer to + the logical device extension list. + + PathId, TargetId and Lun - identify which logical unit on the + SCSI buses. + +Return Value: + + If entry found return miniport driver's logical unit extension. + Else, return NULL. + +--*/ + +{ + PDEVICE_EXTENSION deviceExtension; + PLOGICAL_UNIT_EXTENSION logicalUnit; + + ScsiDebugPrint(1, "ScsiPortGetLogicalUnit: TargetId %d\n", + TargetId); + + // + // Get pointer to port driver device extension. + // + + deviceExtension = (PDEVICE_EXTENSION)HwDeviceExtension -1; + + // + // Get pointer to logical unit list. + // + + logicalUnit = deviceExtension->LogicalUnitList; + + // + // Walk list looking at target id for requested logical unit extension. + // + + while (logicalUnit != NULL) { + + if ((logicalUnit->TargetId == TargetId) && + (logicalUnit->PathId == PathId) && + (logicalUnit->Lun == Lun)) { + + // + // Logical unit extension found. + // Return specific logical unit extension. + // + + return logicalUnit->SpecificLuExtension; + } + + // + // Get next logical unit. + // + + logicalUnit = logicalUnit->NextLogicalUnit; + } + + // + // Requested logical unit extension not found. + // + + return NULL; + +} // end ScsiPortGetLogicalUnit() + +VOID +ScsiPortNotification( + IN SCSI_NOTIFICATION_TYPE NotificationType, + IN PVOID HwDeviceExtension, + ... + ) + +/*++ + +Routine Description: + +Arguments: + +Return Value: + +--*/ + +{ + PDEVICE_EXTENSION deviceExtension = + (PDEVICE_EXTENSION) HwDeviceExtension - 1; + PIO_STACK_LOCATION irpstack; + PLOGICAL_UNIT_EXTENSION logicalUnit; + PSCSI_REQUEST_BLOCK srb; + + + + va_list(ap); + + va_start(ap, HwDeviceExtension); + + ScsiDebugPrint(1,"ScsiPortNotification: enter routine\n"); + + switch (NotificationType) { + + case NextLuRequest: + case NextRequest: + + // + // Start next packet on adapter's queue. + // + + ScsiDebugPrint(2, + "ScsiPortNotification: Start next request\n"); + + deviceExtension->InterruptFlags |= PD_READY_FOR_NEXT_REQUEST; + break; + + case RequestComplete: + + ScsiDebugPrint(2, + "ScsiPortNotification: Request complete\n"); + + srb = va_arg(ap, PSCSI_REQUEST_BLOCK); + + if (srb->SrbStatus == SRB_STATUS_ERROR) { + } + + // + // Link the completed request into a forward-linked list of IRPs. + // + + irpstack = IoGetCurrentIrpStackLocation( + ((PIRP) srb->OriginalRequest) + ); + + irpstack->Parameters.Others.Argument3 = + deviceExtension->CompletedRequests; + + deviceExtension->CompletedRequests = srb->OriginalRequest; + + // + // Set logical unit current request to NULL + // to prevent race condition. + // + + logicalUnit = GetLogicalUnitExtension(deviceExtension, srb->TargetId); + + logicalUnit->CurrentRequest = NULL; + + break; + + case ResetDetected: + + break; + + case CallDisableInterrupts: + + ASSERT(deviceExtension->Flags & PD_DISABLE_INTERRUPTS); + + // + // The mini-port wants us to call the specified routine + // with interrupts disabled. This is done after the current + // HwRequestInterrutp routine completes. Indicate the call is + // needed and save the routine to be called. + // + + deviceExtension->Flags |= PD_DISABLE_CALL_REQUEST; + + deviceExtension->HwRequestInterrupt = va_arg(ap, PHW_INTERRUPT); + + break; + + case CallEnableInterrupts: + + ASSERT(!(deviceExtension->Flags & PD_DISABLE_INTERRUPTS)); + + // + // The mini-port wants us to call the specified routine + // with interrupts enabled this is done from the DPC. + // Disable calls to the interrupt routine, indicate the call is + // needed and save the routine to be called. + // + + deviceExtension->Flags |= PD_DISABLE_INTERRUPTS | PD_ENABLE_CALL_REQUEST; + + deviceExtension->HwRequestInterrupt = va_arg(ap, PHW_INTERRUPT); + + break; + + case RequestTimerCall: + + deviceExtension->HwTimerRequest = va_arg(ap, PHW_INTERRUPT); + deviceExtension->TimerValue = + (va_arg(ap, ULONG) + PD_INTERLOOP_STALL) / PD_INTERLOOP_STALL; + break; + } + + va_end(ap); + + // + // Check to see if the last DPC has been processed yet. If so + // queue another DPC. + // + + ScsiPortCompletionDpc( + NULL, // Dpc + deviceExtension->DeviceObject, // DeviceObject + NULL, // Irp + NULL // Context + ); + + ScsiDebugPrint(1,"ScsiPortNotification completion DPC return\n"); + +} // end ScsiPortNotification() + +/*++ + +VOID +ScsiPortFlushDma( + IN PVOID HwDeviceExtension + ) + + +Routine Description: + + This routine checks to see if the perivious IoMapTransfer has been done + started. If it has not, then the PD_MAP_TRANSER flag is cleared, and the + routine returns; otherwise, this routine schedules a DPC which will call + IoFlushAdapter buffers. + +Arguments: + + HwDeviceExtension - Supplies a the hardware device extension for the + host bus adapter which will be doing the data transfer. + + +Return Value: + + None. + + +{ + + PDEVICE_EXTENSION deviceExtension; + + ScsiDebugPrint(1,"ScsiPortFlushDma: Enter routine.\n"); + + deviceExtension = ((PDEVICE_EXTENSION) HwDeviceExtension) - 1; + + if (deviceExtension->InterruptFlags & PD_MAP_TRANSFER) { + + // + // The transfer has not been started so just clear the map transfer + // flag and return. + // + + deviceExtension->InterruptFlags &= ~PD_MAP_TRANSFER; + return; + } + + deviceExtension->InterruptFlags |= PD_FLUSH_ADAPTER_BUFFERS; + + // + // Check to see if the last DPC has been processed yet. If so + // queue another DPC. + // + + ScsiPortCompletionDpc( + NULL, // Dpc + deviceExtension->DeviceObject, // DeviceObject + NULL, // Irp + NULL // Context + ); + + ScsiDebugPrint(1,"ScsiPortFlushDMA completion DPC return\n"); + return; + +} +--*/ + +/*++ + +VOID +ScsiPortIoMapTransfer( + IN PVOID HwDeviceExtension, + IN PSCSI_REQUEST_BLOCK Srb, + IN PVOID LogicalAddress, + IN ULONG Length + ) + +Routine Description: + + Saves the parameters for the call to IoMapTransfer and schedules the DPC + if necessary. + +Arguments: + + HwDeviceExtension - Supplies a the hardware device extension for the + host bus adapter which will be doing the data transfer. + + Srb - Supplies the particular request that data transfer is for. + + LogicalAddress - Supplies the logical address where the transfer should + begin. + + Length - Supplies the maximum length in bytes of the transfer. + +Return Value: + + None. + + +{ + PDEVICE_EXTENSION deviceExtension; + + ScsiDebugPrint(1,"ScsiPortIoMapTransfer: Enter routine.\n"); + deviceExtension = ((PDEVICE_EXTENSION) HwDeviceExtension) - 1; + + // + // Make sure this host bus adapter has an Dma adapter object. + // + + if (deviceExtension->DmaAdapterObject == NULL) { + ScsiDebugPrint(1,"ScsiPortIoMapTransfer: DmaAdapterObject == NULL.\n"); + // + // No DMA adapter, no work. + // + return; + } + + deviceExtension->MapTransferParameters.Srb = Srb; + deviceExtension->MapTransferParameters.LogicalAddress = LogicalAddress; + deviceExtension->MapTransferParameters.Length = Length; + + deviceExtension->InterruptFlags |= PD_MAP_TRANSFER; + + // + // Check to see if the last DPC has been processed yet. If so + // queue another DPC. + // + + ScsiPortCompletionDpc( + NULL, // Dpc + deviceExtension->DeviceObject, // DeviceObject + NULL, // Irp + NULL // Context + ); + + ScsiDebugPrint(1,"ScsiPortIoMapTransfer completion DPC return\n"); +} // end ScsiPortIoMapTransfer() +--*/ + + +VOID +IssueRequestSense( + IN PDEVICE_EXTENSION deviceExtension, + IN PSCSI_REQUEST_BLOCK FailingSrb + ) + +/*++ + +Routine Description: + + This routine creates a REQUEST SENSE request and uses IoCallDriver to + renter the driver. The completion routine cleans up the data structures + and processes the logical unit queue according to the flags. + + A pointer to failing SRB is stored at the end of the request sense + Srb, so that the completion routine can find it. + +Arguments: + + DeviceExension - Supplies a pointer to the device extension for this + SCSI port. + + FailingSrb - Supplies a pointer to the request that the request sense + is being done for. + +Return Value: + + None. + +--*/ + +{ + PIO_STACK_LOCATION irpstack; + PIRP Irp; + PSCSI_REQUEST_BLOCK Srb; + PCDB cdb; + PVOID *Pointer; + + ScsiDebugPrint(1,"IssueRequestSense: Enter routine\n"); + + // + // Allocate Srb from non-paged pool + // plus room for a pointer to the failing IRP. + // Since this routine is in an error-handling + // path and a shortterm allocation + // NonPagedMustSucceed is requested. + // + + Srb = &RequestSenseSrb.Srb; + + // + // Allocate an IRP to issue the REQUEST SENSE request. + // + + Irp = InitializeIrp( + &RequestSenseSrb, + IRP_MJ_READ, + deviceExtension->DeviceObject, + FailingSrb->SenseInfoBuffer, + FailingSrb->SenseInfoBufferLength + ); + + irpstack = IoGetNextIrpStackLocation(Irp); + + irpstack->MajorFunction = IRP_MJ_SCSI; + + // + // Save the Failing SRB after the request sense Srb. + // + + Pointer = (PVOID *) (Srb+1); + *Pointer = FailingSrb; + + // + // Build the REQUEST SENSE CDB. + // + + Srb->CdbLength = 6; + cdb = (PCDB)Srb->Cdb; + + cdb->CDB6INQUIRY.OperationCode = SCSIOP_REQUEST_SENSE; + cdb->CDB6INQUIRY.LogicalUnitNumber = 0; + cdb->CDB6INQUIRY.Reserved1 = 0; + cdb->CDB6INQUIRY.PageCode = 0; + cdb->CDB6INQUIRY.IReserved = 0; + cdb->CDB6INQUIRY.AllocationLength = + (UCHAR)FailingSrb->SenseInfoBufferLength; + cdb->CDB6INQUIRY.Control = 0; + + // + // Save SRB address in next stack for port driver. + // + + irpstack->Parameters.Others.Argument1 = (PVOID)Srb; + + // + // Set up IRP Address. + // + + Srb->OriginalRequest = Irp; + + Srb->NextSrb = 0; + + // + // Set up SCSI bus address. + // + + Srb->TargetId = FailingSrb->TargetId; + Srb->Lun = FailingSrb->Lun; + Srb->PathId = FailingSrb->PathId; + Srb->Length = sizeof(SCSI_REQUEST_BLOCK); + Srb->Function = SRB_FUNCTION_EXECUTE_SCSI; + + // + // Set timeout value to 2 seconds. + // + + Srb->TimeOutValue = 2; + + // + // Disable auto request sense. + // + + Srb->SenseInfoBufferLength = 0; + + // + // Sense buffer is in stack. + // + + Srb->SenseInfoBuffer = NULL; + + // + // Set read and bypass frozen queue bits in flags. + // + + // + // Set a speical flags to indicate the logical unit queue should be by + // passed and that no queue processing should be done when the request + // completes. + // + + Srb->SrbFlags = SRB_FLAGS_DATA_IN | SRB_FLAGS_BYPASS_FROZEN_QUEUE | + SRB_FLAGS_DISABLE_SYNCH_TRANSFER | SRB_FLAGS_DISABLE_DISCONNECT; + + Srb->DataBuffer = FailingSrb->SenseInfoBuffer; + + // + // Set the transfer length. + // + + Srb->DataTransferLength = FailingSrb->SenseInfoBufferLength; + + // + // Zero out status. + // + + Srb->ScsiStatus = Srb->SrbStatus = 0; + + (VOID)IoCallDriver(deviceExtension->DeviceObject, Irp); + + ScsiPortInternalCompletion(deviceExtension->DeviceObject, Irp, Srb); + + return; + +} // end IssueRequestSense() + + +VOID +ScsiPortInternalCompletion( + PDEVICE_OBJECT DeviceObject, + PIRP Irp, + PVOID Context + ) + +/*++ + +Routine Description: + +Arguments: + + Device object + IRP + Context - pointer to SRB + +Return Value: + + None. + +--*/ + +{ + PSCSI_REQUEST_BLOCK srb = Context; + PIO_STACK_LOCATION irpstack = IoGetCurrentIrpStackLocation(Irp); + PSCSI_REQUEST_BLOCK failingSrb; + PIRP failingIrp; + + UNREFERENCED_PARAMETER(DeviceObject); + + ScsiDebugPrint(1,"ScsiPortInternalCompletion: Enter routine\n"); + + // + // Request sense completed. If successful or data over/underrun + // get the failing SRB and indicate that the sense information + // is valid. The class driver will check for underrun and determine + // if there is enough sense information to be useful. + // + + if ((SRB_STATUS(srb->SrbStatus) == SRB_STATUS_SUCCESS) || + (SRB_STATUS(srb->SrbStatus) == SRB_STATUS_DATA_OVERRUN)) { + + // + // Get a pointer to failing Irp and Srb. + // + + failingSrb = *((PVOID *) (srb+1)); + failingIrp = failingSrb->OriginalRequest; + + // + // Report sense buffer is valid. + // + + failingSrb->SrbStatus |= SRB_STATUS_AUTOSENSE_VALID; + + // + // Copy bytes transferred to failing SRB + // request sense length field to communicate + // to the class drivers the number of valid + // sense bytes. + // + + failingSrb->SenseInfoBufferLength = (UCHAR) srb->DataTransferLength; + + } + +} // ScsiPortInternalCompletion() + + +VOID +ScsiPortTickHandler( + IN PDEVICE_OBJECT DeviceObject, + IN PVOID Context + ) + +/*++ + +Routine Description: + +Arguments: + +Return Value: + + None. + +--*/ + +{ + PDEVICE_EXTENSION deviceExtension = + (PDEVICE_EXTENSION) DeviceObject->DeviceExtension; + PLOGICAL_UNIT_EXTENSION logicalUnit; + + UNREFERENCED_PARAMETER(Context); + + logicalUnit = deviceExtension->LogicalUnitList; + + ScsiDebugPrint(1,"ScsiPortTickHandler: Enter routine.\n"); + // + // NOTE: The use of Current request needs to be synchronized with the + // clearing of current request. + // + + while (logicalUnit != NULL) { + + // + // Check for busy requests. + // + + if (logicalUnit->Flags & PD_LOGICAL_UNIT_IS_BUSY) { + + ScsiDebugPrint(1,"ScsiPortTickHandler: Retrying busy status request\n"); + + // + // Clear the busy flag and retry the request. + // + + logicalUnit->Flags &= ~PD_LOGICAL_UNIT_IS_BUSY; + + ScsiPortStartIo(DeviceObject, logicalUnit->CurrentRequest); + + } else if (logicalUnit->RequestTimeoutCounter == 0) { + + // + // Request timed out. + // + + ScsiDebugPrint(1, "ScsiPortTickHandler: Request timed out\n"); + + // + // Reset request timeout counter to unused state. + // + + logicalUnit->RequestTimeoutCounter = -1; + + // + // Build and send request to abort command. + // + + IssueAbortRequest(deviceExtension, logicalUnit->CurrentRequest); + } else if (logicalUnit->RequestTimeoutCounter != -1) { + + ScsiDebugPrint(1, "ScsiPortTickHandler: Timeout value %lx\n",logicalUnit->RequestTimeoutCounter); + logicalUnit->RequestTimeoutCounter--; + } + + logicalUnit = logicalUnit->NextLogicalUnit; + } + + return; + +} // end ScsiPortTickHandler() + + +VOID +IssueAbortRequest( + IN PDEVICE_EXTENSION DeviceExtension, + IN PIRP FailingIrp + ) + +/*++ + +Routine Description: + + A request timed out and to clear the request at the HBA + an ABORT request is issued. But first, if the request + that timed out was an ABORT command, then reset the + adapter instead. + +Arguments: + + DeviceExension - Supplies a pointer to the device extension for this + SCSI port. + + FailingIrp - Supplies a pointer to the request that is to be aborted. + +Return Value: + + None. + +--*/ + +{ + + ULONG j; + ScsiDebugPrint(1,"IssueAbortRequest: Enter routine\n"); + + + // + // A request to abort failed. + // Need to reset the adapter. + // + + ScsiDebugPrint(1,"IssueAbort: Request timed out resetting the bus.\n"); + + + if (!DeviceExtension->HwReset( + DeviceExtension->HwDeviceExtension, + 0)){ + + ScsiDebugPrint(1,"Reset SCSI bus failed\n"); + } + + // + // Call the interupt handler for a few microseconds to clear any reset + // interrupts. + // + + for (j = 0; j < 100 * 100; j++) { + + FwStallExecution(10); + DeviceExtension->HwInterrupt(DeviceExtension->HwDeviceExtension); + + } + + // + // Wait 2 seconds for the devices to recover after the reset. + // + + FwStallExecution(2 * 100 * 1000); + + return; + + +} // end IssueAbortRequest() + +/*++ + +BOOLEAN +SpGetInterruptState( + IN PVOID ServiceContext + ) + + +Routine Description: + + This routine saves the InterruptFlags, MapTransferParameters and + CompletedRequests fields and clears the InterruptFlags. + +Arguments: + + ServiceContext - Supplies a pointer to the device extension for this + SCSI port. + +Return Value: + + Always returns TRUE. + +Notes: + + Called via KeSynchronizeExecution. + +{ + PDEVICE_EXTENSION deviceExtension = ServiceContext; + + // + // Move the interrupt state to save area. + // + + deviceExtension->InterruptFlags = deviceExtension->InterruptFlags; + deviceExtension->CompletedRequests = deviceExtension->CompletedRequests; + deviceExtension->MapTransferParameters = deviceExtension->MapTransferParameters; + + // + // Clear the interrupt state. + // + + deviceExtension->InterruptFlags = 0; + deviceExtension->CompletedRequests = NULL; + + return(TRUE); +} +--*/ +/*++ + +VOID +ScsiPortLogError( + IN PVOID HwDeviceExtension, + IN PSCSI_REQUEST_BLOCK Srb OPTIONAL, + IN UCHAR PathId, + IN UCHAR TargetId, + IN UCHAR Lun, + IN ULONG ErrorCode, + IN ULONG UniqueId + ) + + +Routine Description: + + This routine allocates an error log entry, copies the supplied text + to it, and requests that it be written to the error log file. + +Arguments: + + DeviceExtenson - Supplies the HBA mini-port driver's adapter data storage. + + TargetId, Lun and PathId - specify device address on a SCSI bus. + + ErrorCode - Supplies an error code indicating the type of error. + + UniqueId - Supplies a unique identifier for the error. + +Return Value: + + None. + + +{ + PCHAR errorCodeString; + + switch (ErrorCode) { + case SP_BUS_PARITY_ERROR: + errorCodeString = "SCSI bus partity error"; + break; + + case SP_UNEXPECTED_DISCONNECT: + errorCodeString = "Unexpected disconnect"; + break; + + case SP_INVALID_RESELECTION: + errorCodeString = "Invalid reselection"; + break; + + case SP_BUS_TIME_OUT: + errorCodeString = "SCSI bus time out"; + break; + + case SP_PROTOCOL_ERROR: + errorCodeString = "SCSI protocol error"; + break; + + case SP_INTERNAL_ADAPTER_ERROR: + errorCodeString = "Internal adapter error"; + break; + + default: + errorCodeString = "Unknown error code"; + break; + + } + + ScsiDebugPrint((0,"\n\nLogErrorEntry: Logging SCSI error packet. ErrorCode = %s.\n", + errorCodeString + )); + ScsiDebugPrint((0, + "PathId = %2x, TargetId = %2x, Lun = %2x, UniqueId = %x.\n\n", + PathId, + TargetId, + Lun, + UniqueId + )); + + + return; + +} // end ScsiPortLogError() +--*/ + + +VOID +ScsiPortCompleteRequest( + IN PVOID HwDeviceExtension, + IN UCHAR PathId, + IN UCHAR TargetId, + IN UCHAR Lun, + IN UCHAR SrbStatus + ) + +/*++ + +Routine Description: + + Complete all active requests for the specified logical unit. + +Arguments: + + DeviceExtenson - Supplies the HBA mini-port driver's adapter data storage. + + TargetId, Lun and PathId - specify device address on a SCSI bus. + + SrbStatus - Status to be returned in each completed SRB. + +Return Value: + + None. + +--*/ + +{ + PDEVICE_EXTENSION deviceExtension = + ((PDEVICE_EXTENSION) HwDeviceExtension) - 1; + PSCSI_REQUEST_BLOCK Srb; + PSCSI_REQUEST_BLOCK failingSrb; + PLOGICAL_UNIT_EXTENSION luExtension; + PIRP nextIrp; + PIO_STACK_LOCATION irpstack; + + UNREFERENCED_PARAMETER(PathId); + UNREFERENCED_PARAMETER(Lun); + + if (TargetId == (UCHAR)(-1)) { + + // + // Complete requests for all units on this bus. + // + + luExtension = deviceExtension->LogicalUnitList; + + while (luExtension != NULL) { + + ScsiDebugPrint(2, + "ScsiPortCompleteRequest: Complete requests for targetid %d\n", + luExtension->TargetId); + + // + // Complete requests until queue is empty. + // + + if ((nextIrp = luExtension->CurrentRequest) != NULL && + !(luExtension->Flags & PD_LOGICAL_UNIT_IS_BUSY)) { + + ScsiDebugPrint(3,"ScsiPortCompleteRequest: Current request %lx\n", + nextIrp); + + // + // Get SRB address from current IRP stack. + // + + irpstack = IoGetCurrentIrpStackLocation(nextIrp); + + Srb = (PSCSI_REQUEST_BLOCK)irpstack->Parameters.Others.Argument1; + + // + // Just in case this is an abort request, + // get pointer to failingSrb. + // + + failingSrb = Srb->NextSrb; + + // + // Update SRB status. + // + + Srb->SrbStatus = SrbStatus; + + // + // Indicate no bytes transferred. + // + + Srb->DataTransferLength = 0; + + // + // Set IRP status. + // + + nextIrp->IoStatus.Status = STATUS_UNSUCCESSFUL; + + // + // Move bytes transferred to IRP. + // + + nextIrp->IoStatus.Information = Srb->DataTransferLength; + + // + // Call notification routine. + // + + ScsiPortNotification(RequestComplete, + (PVOID)HwDeviceExtension, + Srb); + + if (failingSrb) { + + // + // This was an abort request. The failing + // SRB must also be completed. + // + + failingSrb->SrbStatus = SrbStatus; + failingSrb->DataTransferLength = 0; + + // + // Get IRP from SRB. + // + + nextIrp = failingSrb->OriginalRequest; + + // + // Set IRP status. + // + + nextIrp->IoStatus.Status = STATUS_UNSUCCESSFUL; + + // + // Move bytes transferred to IRP. + // + + nextIrp->IoStatus.Information = + failingSrb->DataTransferLength; + + // + // Call notification routine. + // + + ScsiPortNotification(RequestComplete, + (PVOID)HwDeviceExtension, + failingSrb); + } + + } // end if + + luExtension = luExtension->NextLogicalUnit; + + } // end while + + } else { + + // + // Complete all requests for this logical unit. + // + + ScsiDebugPrint(2, + "ScsiPortCompleteRequest: Complete requests for targetid %d\n", + TargetId); + + + luExtension = + GetLogicalUnitExtension(deviceExtension, TargetId); + + // + // Complete requests until queue is empty. + // + + if ((nextIrp = luExtension->CurrentRequest) != NULL) { + + ScsiDebugPrint(3,"ScsiPortCompleteRequest: Current request %lx\n", + nextIrp); + + // + // Get SRB address from current IRP stack. + // + + irpstack = IoGetCurrentIrpStackLocation(nextIrp); + + Srb = (PSCSI_REQUEST_BLOCK)irpstack->Parameters.Others.Argument1; + + // + // Update SRB status. + // + + Srb->SrbStatus = SrbStatus; + + // + // Indicate no bytes transferred. + // + + Srb->DataTransferLength = 0; + + // + // Set IRP status. + // + + nextIrp->IoStatus.Status = STATUS_UNSUCCESSFUL; + + // + // Move bytes transferred to IRP. + // + + nextIrp->IoStatus.Information = Srb->DataTransferLength; + + // + // Call notification routine. + // + + ScsiPortNotification(RequestComplete, + (PVOID)HwDeviceExtension, + Srb); + + } // end while + + } // end if ... else + + return; + + +} // end ScsiPortCompleteRequest() + + +VOID +ScsiPortMoveMemory( + IN PVOID WriteBuffer, + IN PVOID ReadBuffer, + IN ULONG Length + ) + +/*++ + +Routine Description: + + Copy from one buffer into another. + +Arguments: + + ReadBuffer - source + WriteBuffer - destination + Length - number of bytes to copy + +Return Value: + + None. + +--*/ + +{ + RtlMoveMemory(WriteBuffer, ReadBuffer, Length); + +} // end ScsiPortMoveMemory() + + +VOID +ScsiPortStallExecution( + ULONG Delay + ) +/*++ + +Routine Description: + + Wait number of microseconds in tight processor loop. + +Arguments: + + Delay - number of microseconds to wait. + +Return Value: + + None. + +--*/ + +{ + FwStallExecution(Delay); + +} // end ScsiPortStallExecution() + + +PLOGICAL_UNIT_EXTENSION +GetLogicalUnitExtension( + PDEVICE_EXTENSION deviceExtension, + UCHAR TargetId + ) + +/*++ + +Routine Description: + + Walk logical unit extension list looking for + extension with matching target id. + +Arguments: + + deviceExtension + TargetId + +Return Value: + + Requested logical unit extension if found, + else NULL. + +--*/ + +{ + PLOGICAL_UNIT_EXTENSION logicalUnit = deviceExtension->LogicalUnitList; + + while (logicalUnit != NULL) { + + if (logicalUnit->TargetId == TargetId) { + + return logicalUnit; + } + + logicalUnit = logicalUnit->NextLogicalUnit; + } + + // + // Logical unit extension not found. + // + + return (PLOGICAL_UNIT_EXTENSION)NULL; + +} // end GetLogicalUnitExtension() + +#if DBG + +VOID +ScsiDebugPrint( + ULONG DebugPrintLevel, + PCCHAR DebugMessage, + ... + ) + +/*++ + +Routine Description: + + Debug print for all SCSI drivers + +Arguments: + + Debug print level between 0 and 3, with 3 being the most verbose. + +Return Value: + + None + +--*/ + +{ + + va_list ap; + + UCHAR ScsiBuffer[256]; + va_start(ap, DebugMessage); + + if (DebugPrintLevel <= ScsiDebug) { + + vsprintf(ScsiBuffer, DebugMessage, ap); + + DbgPrint(ScsiBuffer); + } + + va_end(ap); + +} // end ScsiDebugPrint() +#endif + + +/*++ + +UCHAR +ScsiPortReadPortUchar( + IN PUCHAR Port + ) + + +Routine Description: + + Read from the specificed port address. + +Arguments: + + Port - Supplies a pointer to the port address. + +Return Value: + + Returns the value read from the specified port address. + + +{ + + return(READ_REGISTER_UCHAR(Port)); + +} + +USHORT +ScsiPortReadPortUshort( + IN PUSHORT Port + ) + + +Routine Description: + + Read from the specificed port address. + +Arguments: + + Port - Supplies a pointer to the port address. + +Return Value: + + Returns the value read from the specified port address. + + +{ + + return(READ_REGISTER_USHORT(Port)); + +} + +ULONG +ScsiPortReadPortUlong( + IN PULONG Port + ) + + +Routine Description: + + Read from the specificed port address. + +Arguments: + + Port - Supplies a pointer to the port address. + +Return Value: + + Returns the value read from the specified port address. + + +{ + + return(READ_REGISTER_ULONG(Port)); + +} + +UCHAR +ScsiPortReadRegisterUchar( + IN PUCHAR Register + ) + + +Routine Description: + + Read from the specificed register address. + +Arguments: + + Register - Supplies a pointer to the register address. + +Return Value: + + Returns the value read from the specified register address. + + +{ + + return(READ_REGISTER_UCHAR(Register)); + +} + +USHORT +ScsiPortReadRegisterUshort( + IN PUSHORT Register + ) + + +Routine Description: + + Read from the specificed register address. + +Arguments: + + Register - Supplies a pointer to the register address. + +Return Value: + + Returns the value read from the specified register address. + + +{ + + return(READ_REGISTER_USHORT(Register)); + +} + +ULONG +ScsiPortReadRegisterUlong( + IN PULONG Register + ) + + +Routine Description: + + Read from the specificed register address. + +Arguments: + + Register - Supplies a pointer to the register address. + +Return Value: + + Returns the value read from the specified register address. + + +{ + + return(READ_REGISTER_ULONG(Register)); + +} + +VOID +ScsiPortReadRegisterBufferUchar( + IN PUCHAR Register, + IN PUCHAR Buffer, + IN ULONG Count + ) + + +Routine Description: + + Read a buffer of unsigned bytes from the specified register address. + +Arguments: + + Register - Supplies a pointer to the port address. + Buffer - Supplies a pointer to the data buffer area. + Count - The count of items to move. + +Return Value: + + None + + +{ + + READ_REGISTER_BUFFER_UCHAR(Register, Buffer, Count); + +} + +VOID +ScsiPortReadRegisterBufferUshort( + IN PUSHORT Register, + IN PUSHORT Buffer, + IN ULONG Count + ) + + +Routine Description: + + Read a buffer of unsigned shorts from the specified register address. + +Arguments: + + Register - Supplies a pointer to the port address. + Buffer - Supplies a pointer to the data buffer area. + Count - The count of items to move. + +Return Value: + + None + + +{ + + READ_REGISTER_BUFFER_USHORT(Register, Buffer, Count); + +} + +VOID +ScsiPortReadRegisterBufferUlong( + IN PULONG Register, + IN PULONG Buffer, + IN ULONG Count + ) + + +Routine Description: + + Read a buffer of unsigned longs from the specified register address. + +Arguments: + + Register - Supplies a pointer to the port address. + Buffer - Supplies a pointer to the data buffer area. + Count - The count of items to move. + +Return Value: + + None + + +{ + + READ_REGISTER_BUFFER_ULONG(Register, Buffer, Count); + +} + +VOID +ScsiPortWritePortUchar( + IN PUCHAR Port, + IN UCHAR Value + ) + + +Routine Description: + + Write to the specificed port address. + +Arguments: + + Port - Supplies a pointer to the port address. + + Value - Supplies the value to be written. + +Return Value: + + None + + +{ + + WRITE_REGISTER_UCHAR(Port, Value); + +} + +VOID +ScsiPortWritePortUshort( + IN PUSHORT Port, + IN USHORT Value + ) + + +Routine Description: + + Write to the specificed port address. + +Arguments: + + Port - Supplies a pointer to the port address. + + Value - Supplies the value to be written. + +Return Value: + + None + + +{ + + WRITE_REGISTER_USHORT(Port, Value); + +} + +VOID +ScsiPortWritePortUlong( + IN PULONG Port, + IN ULONG Value + ) + + +Routine Description: + + Write to the specificed port address. + +Arguments: + + Port - Supplies a pointer to the port address. + + Value - Supplies the value to be written. + +Return Value: + + None + + +{ + + WRITE_REGISTER_ULONG(Port, Value); + +} +--*/ +/*++ + +VOID +ScsiPortWriteRegisterUchar( + IN PUCHAR Register, + IN UCHAR Value + ) + + +Routine Description: + + Write to the specificed register address. + +Arguments: + + Register - Supplies a pointer to the register address. + + Value - Supplies the value to be written. + +Return Value: + + None + + +{ + + WRITE_REGISTER_UCHAR(Register, Value); + +} +--*/ +/*++ + +VOID +ScsiPortWriteRegisterUshort( + IN PUSHORT Register, + IN USHORT Value + ) + + +Routine Description: + + Write to the specificed register address. + +Arguments: + + Register - Supplies a pointer to the register address. + + Value - Supplies the value to be written. + +Return Value: + + None + + +{ + + WRITE_REGISTER_USHORT(Register, Value); + +} +--*/ +/*++ + +VOID +ScsiPortWriteRegisterUlong( + IN PULONG Register, + IN ULONG Value + ) + + +Routine Description: + + Write to the specificed register address. + +Arguments: + + Register - Supplies a pointer to the register address. + + Value - Supplies the value to be written. + +Return Value: + + None + + +{ + + WRITE_REGISTER_ULONG(Register, Value); + +} +--*/ +/*++ + +VOID +ScsiPortWriteRegisterBufferUchar( + IN PUCHAR Register, + IN PUCHAR Buffer, + IN ULONG Count + ) + + +Routine Description: + + Write a buffer of unsigned bytes from the specified register address. + +Arguments: + + Register - Supplies a pointer to the port address. + Buffer - Supplies a pointer to the data buffer area. + Count - The count of items to move. + +Return Value: + + None + + +{ + + WRITE_REGISTER_BUFFER_UCHAR(Register, Buffer, Count); + +} +--*/ +/*++ + +VOID +ScsiPortWriteRegisterBufferUshort( + IN PUSHORT Register, + IN PUSHORT Buffer, + IN ULONG Count + ) + + +Routine Description: + + Write a buffer of unsigned shorts from the specified register address. + +Arguments: + + Register - Supplies a pointer to the port address. + Buffer - Supplies a pointer to the data buffer area. + Count - The count of items to move. + +Return Value: + + None + + +{ + + WRITE_REGISTER_BUFFER_USHORT(Register, Buffer, Count); + +} +--*/ +/*++ + +VOID +ScsiPortWriteRegisterBufferUlong( + IN PULONG Register, + IN PULONG Buffer, + IN ULONG Count + ) + + +Routine Description: + + Write a buffer of unsigned longs from the specified register address. + +Arguments: + + Register - Supplies a pointer to the port address. + Buffer - Supplies a pointer to the data buffer area. + Count - The count of items to move. + +Return Value: + + None + + +{ + + WRITE_REGISTER_BUFFER_ULONG(Register, Buffer, Count); + +} +--*/ + +SCSI_PHYSICAL_ADDRESS +ScsiPortConvertUlongToPhysicalAddress( + ULONG UlongAddress + ) + +{ + SCSI_PHYSICAL_ADDRESS physicalAddress; + + physicalAddress.HighPart = 0; + physicalAddress.LowPart = UlongAddress; + return(physicalAddress); +} + +#undef ScsiPortConvertPhysicalAddressToUlong + +ULONG +ScsiPortConvertPhysicalAddressToUlong( + SCSI_PHYSICAL_ADDRESS Address + ) +{ + + return(Address.LowPart); +} + + + +PIRP +InitializeIrp( + PFULL_SCSI_REQUEST_BLOCK FullSrb, + CCHAR MajorFunction, + PVOID DeviceObject, + PVOID Buffer, + ULONG Length + ) +/*++ + +Routine Description: + + This funcition builds an IRP for use by the SCSI port driver and builds a + MDL list. + +Arguments: + + FullSrb - Supplies a pointer to the full srb structure which contains the + Irp and Mdl. + + MajorFunction - Supplies the major function code to initialize the Irp + entry. + + DeviceObject - Supplies the device Object pointer to initialize the Irp + with. + + Buffer - Supplies the virual address of the buffer for which the + Mdl should be built. + + Length - Supplies the size of buffer for which the Mdl should be built. + +Return Value: + + Returns a pointer to the initialized IRP. + +--*/ + +{ + PIRP irp; + PMDL mdl; + PULONG pageFrame; + ULONG frameNumber; + ULONG index; + ULONG numberOfPages; + + irp = &FullSrb->Irp; + mdl = &FullSrb->Mdl; + + ScsiDebugPrint(1,"ScsiPortInitializeIrp: Enter routine.\n"); + irp->Tail.Overlay.CurrentStackLocation = &FullSrb->IrpStack[IRP_STACK_SIZE]; + + if (Buffer != NULL && Length != 0) { + + // + // Build the memory descriptor list. + // + + irp->MdlAddress = mdl; + mdl->Next = NULL; + mdl->Size = sizeof(MDL) + + ADDRESS_AND_SIZE_TO_SPAN_PAGES(Buffer, Length) * sizeof(ULONG); + mdl->StartVa = (PVOID)PAGE_ALIGN(Buffer); + mdl->ByteCount = Length; + mdl->ByteOffset = BYTE_OFFSET(Buffer); + mdl->MappedSystemVa = Buffer; + mdl->MdlFlags = MDL_MAPPED_TO_SYSTEM_VA; + pageFrame = (PULONG)(mdl + 1); + frameNumber = RtlLargeIntegerShiftRight( + MmGetPhysicalAddress(mdl->StartVa), PAGE_SHIFT).LowPart; + numberOfPages = (mdl->ByteCount + + mdl->ByteOffset + PAGE_SIZE - 1) >> PAGE_SHIFT; + for (index = 0; index < numberOfPages; index += 1) { + *pageFrame++ = frameNumber++; + } + + } else { + irp->MdlAddress = NULL; + } + + return(irp); +} + +PVOID +ScsiPortGetDeviceBase( + IN PVOID HwDeviceExtension, + IN INTERFACE_TYPE BusType, + IN ULONG SystemIoBusNumber, + SCSI_PHYSICAL_ADDRESS IoAddress, + ULONG NumberOfBytes, + BOOLEAN InMemorySpace + ) + +/*++ + +Routine Description: + + This routine maps an IO address to system address space. + Use ScsiPortFreeDeviceBase to unmap address. + +Arguments: + + HwDeviceExtension - used to find port device extension. + BusType - what type of bus - eisa, mca, isa + SystemIoBusNumber - which IO bus (for machines with multiple buses). + IoAddress - base device address to be mapped. + NumberOfBytes - number of bytes for which address is valid. + +Return Value: + + Mapped address + +--*/ + +{ + PHYSICAL_ADDRESS cardAddress; + ULONG addressSpace = InMemorySpace; + PVOID mappedAddress; + + HalTranslateBusAddress( + BusType, // AdapterInterfaceType + SystemIoBusNumber, // SystemIoBusNumber + IoAddress, // Bus Address + &addressSpace, // AddressSpace + &cardAddress // Translated address + ); + + // + // Map the device base address into the virtual address space + // if the address is in memory space. + // + + if (!addressSpace) { + + mappedAddress = MmMapIoSpace(cardAddress, + NumberOfBytes, + FALSE); + + + } else { + + mappedAddress = (PVOID)cardAddress.LowPart; + } + + return mappedAddress; + +} // end ScsiPortGetDeviceBase() + +VOID +ScsiPortFreeDeviceBase( + IN PVOID HwDeviceExtension, + IN PVOID MappedAddress + ) + +/*++ + +Routine Description: + + This routine unmaps an IO address that has been previously mapped + to system address space using ScsiPortGetDeviceBase(). + +Arguments: + + HwDeviceExtension - used to find port device extension. + MappedAddress - address to unmap. + NumberOfBytes - number of bytes mapped. + InIoSpace - addresses in IO space don't get mapped. + +Return Value: + + None + +--*/ + +{ + UNREFERENCED_PARAMETER(HwDeviceExtension); + UNREFERENCED_PARAMETER(MappedAddress); + + return; + +} // end ScsiPortFreeDeviceBase() + +ARC_STATUS +GetAdapterCapabilities( + IN PDEVICE_OBJECT PortDeviceObject, + OUT PIO_SCSI_CAPABILITIES *PortCapabilities + ) + +/*++ + +Routine Description: + +Arguments: + +Return Value: + + Status is returned. + +--*/ + +{ + *PortCapabilities = &((PDEVICE_EXTENSION)PortDeviceObject->DeviceExtension) + ->Capabilities; + + return(ESUCCESS); +} // end GetAdapterCapabilities() + +/*++ + +ARC_STATUS +GetInquiryData( + IN PDEVICE_OBJECT PortDeviceObject, + OUT PSCSI_CONFIGURATION_INFO *ConfigInfo + ) + + +Routine Description: + + This routine sends a request to a port driver to return + configuration information. + +Arguments: + + The address of the configuration information is returned in + the formal parameter ConfigInfo. + +Return Value: + + Status is returned. + +{ + *ConfigInfo = ((PDEVICE_EXTENSION)PortDeviceObject->DeviceExtension) + ->ScsiInfo; + return(ESUCCESS); +} // end GetInquiryData() +--*/ + +NTSTATUS +SpInitializeConfiguration( + IN PDEVICE_EXTENSION DeviceExtension, + IN PHW_INITIALIZATION_DATA HwInitData, + OUT PPORT_CONFIGURATION_INFORMATION ConfigInfo, + IN BOOLEAN InitialCall + ) +/*++ + +Routine Description: + + This routine initializes the port configuration information structure. + Any necessary information is extracted from the registery. + +Arguments: + + DeviceExtension - Supplies the device extension. + + HwInitializationData - Supplies the initial miniport data. + + ConfigInfo - Supplies the configuration information to be + initialized. + + InitialCall - Indicates that this is first call to this function. + If InitialCall is FALSE, then the perivous configuration information + is used to determine the new information. + +Return Value: + + Returns a status indicating the success or fail of the initializaiton. + +--*/ + +{ + ULONG j; + + // + // If this is the initial call then zero the information and set + // the structure to the uninitialized values. + // + + if (InitialCall) { + + RtlZeroMemory(ConfigInfo, sizeof(PORT_CONFIGURATION_INFORMATION)); + + ConfigInfo->Length = sizeof(PORT_CONFIGURATION_INFORMATION); + ConfigInfo->AdapterInterfaceType = HwInitData->AdapterInterfaceType; + ConfigInfo->InterruptMode = Latched; + ConfigInfo->MaximumTransferLength = 0xffffffff; + ConfigInfo->NumberOfPhysicalBreaks = 0xffffffff; + ConfigInfo->DmaChannel = 0xffffffff; + ConfigInfo->NumberOfAccessRanges = HwInitData->NumberOfAccessRanges; + + for (j = 0; j < 8; j++) { + ConfigInfo->InitiatorBusId[j] = 7; + } +#ifdef DUO + if (HwInitData->AdapterInterfaceType != Internal) { + return(STATUS_DEVICE_DOES_NOT_EXIST); + } +#else + if (HwInitData->AdapterInterfaceType != Eisa) { + return(STATUS_DEVICE_DOES_NOT_EXIST); + } +#endif + return(STATUS_SUCCESS); + + } else { + + return(STATUS_DEVICE_DOES_NOT_EXIST); + } +} + + +NTSTATUS +SpGetCommonBuffer( + PDEVICE_EXTENSION DeviceExtension, + ULONG NonCachedExtensionSize + ) +/*++ + +Routine Description: + + This routine determines the required size of the common buffer. Allocates + the common buffer and finally sets up the srb extension zone. This routine + expects that the adapter object has already been allocated. + +Arguments: + + DeviceExtension - Supplies a pointer to the device extension. + + NonCachedExtensionSize - Supplies the size of the noncached device + extension for the mini-port driver. + +Return Value: + + Returns the status of the allocate operation. + +--*/ + +{ + PVOID buffer; + ULONG length; + ULONG blockSize; + PTRANSLATION_ENTRY DmaMapRegister; + ULONG BasePage; + ULONG NumberOfPages; + // + // Calculate the block size for the zone elements based on the Srb + // Extension. + // + + blockSize = DeviceExtension->SrbExtensionSize; + + // + // Last three bits of blocksize must be zero. + // Round blocksize up. + // + + blockSize = (blockSize + 7) & ~7; + + length = NonCachedExtensionSize + blockSize * MINIMUM_SRB_EXTENSIONS; + + // + // Round the length up to a page size, since HalGetCommonBuffer allocates + // in pages anyway. + // + + length = ROUND_TO_PAGES(length); + + // + // Allocate one page for noncached deviceextension + // and srbextension zoned pool. + // + + if (!(buffer = MmAllocateNonCachedMemory(length))) { + + ScsiDebugPrint(1, + "ScsiPortInitialize: Could not allocate page of noncached pool\n"); + + return ENOMEM; + } + + // + // Determine length and starting address of zone. + // If noncached device extension required then + // subtract size from page leaving rest for zone. + // + + length -= NonCachedExtensionSize; + + DeviceExtension->NonCachedExtension = (PUCHAR)buffer + length; + + if (DeviceExtension->SrbExtensionSize) { + + // + // Get block size. + // + + blockSize = DeviceExtension->SrbExtensionSize; + + // + // Record starting virtual address of zone. + // + + DeviceExtension->SrbExtensionZonePool = buffer; + DeviceExtension->SrbExtensionPointer = buffer; + DeviceExtension->SrbExtensionSize = blockSize; + + + // + // Get physical address of zone. + // + + DeviceExtension->PhysicalZoneBase = + MmGetPhysicalAddress(buffer).LowPart; + + } else { + DeviceExtension->SrbExtensionZonePool = NULL; + } + + // + // Map the Non cached buffer. Logical = Physical. + // + + DmaMapRegister = (PTRANSLATION_ENTRY)(READ_REGISTER_ULONG(&DMA_CONTROL->TranslationBase.Long) | KSEG1_BASE); + + + BasePage = (ULONG) (DeviceExtension->NonCachedExtension); + + NumberOfPages = (((BasePage - KSEG1_BASE) & 0xFFF) + NonCachedExtensionSize + PAGE_SIZE - 1) >> PAGE_SHIFT; + + BasePage &= 0x0FFFF000; + + for (;NumberOfPages;NumberOfPages--) { + ScsiDebugPrint(1,"SpGetCommonBuffer Mapping %lx into %lx\n", BasePage,DmaMapRegister+(BasePage >> PAGE_SHIFT)); + (DmaMapRegister+(BasePage >> PAGE_SHIFT))->PageFrame = BasePage; + BasePage += PAGE_SIZE; + } + + return(ESUCCESS); +} + +PVOID +ScsiPortGetUncachedExtension( + IN PVOID HwDeviceExtension, + IN PPORT_CONFIGURATION_INFORMATION ConfigInfo, + IN ULONG NumberOfBytes + ) +/*++ + +Routine Description: + + This function allocates a common buffer to be used as the uncached device + extension for the mini-port driver. This function will also allocate any + required SRB extensions. The DmaAdapter is allocated if it has not been + allocated previously. + +Arguments: + + DeviceExtension - Supplies a pointer to the mini-ports device extension. + + ConfigInfo - Supplies a pointer to the partially initialized configuraiton + information. This is used to get an DMA adapter object. + + NumberOfBytes - Supplies the size of the extension which needs to be + allocated + +Return Value: + + A pointer to the uncached device extension or NULL if the extension could + not be allocated or was previously allocated. + +--*/ + +{ + PDEVICE_EXTENSION deviceExtension = + ((PDEVICE_EXTENSION) HwDeviceExtension) - 1; + NTSTATUS status; + + // + // Make sure that an common buffer has not already been allocated. + // + + if (deviceExtension->SrbExtensionZonePool != NULL) { + return(NULL); + } + + // + // Allocate the common buffer. + // + + status = SpGetCommonBuffer( deviceExtension, NumberOfBytes); + + if (status != ESUCCESS) { + return(NULL); + } + + return(deviceExtension->NonCachedExtension); +} + +PSCSI_REQUEST_BLOCK +ScsiPortGetSrb( + IN PVOID HwDeviceExtension, + IN UCHAR PathId, + IN UCHAR TargetId, + IN UCHAR Lun, + IN LONG QueueTag + ) + +/*++ + +Routine Description: + + This routine retrieves an active SRB for a particuliar logical unit. + +Arguments: + + HwDeviceExtension + PathId, TargetId, Lun - identify logical unit on SCSI bus. + QueueTag - -1 indicates request is not tagged. + +Return Value: + + SRB, if one exists. Otherwise, NULL. + +--*/ + +{ + PDEVICE_EXTENSION deviceExtension = + ((PDEVICE_EXTENSION) HwDeviceExtension) - 1; + PLOGICAL_UNIT_EXTENSION luExtension; + PIRP irp; + PIO_STACK_LOCATION irpstack; + + + luExtension = GetLogicalUnitExtension(deviceExtension, TargetId); + + + if (luExtension == NULL) { + return(NULL); + } + + irp = luExtension->CurrentRequest; + irpstack = IoGetCurrentIrpStackLocation(irp); + return ((PSCSI_REQUEST_BLOCK)irpstack->Parameters.Others.Argument1); + +} // end ScsiPortGetSrb() + +/*++ + +VOID +ScsiPortReadPortBufferUchar( + IN PUCHAR Port, + IN PUCHAR Buffer, + IN ULONG Count + ) + + +Routine Description: + + Read a buffer of unsigned bytes from the specified port address. + +Arguments: + + Port - Supplies a pointer to the port address. + Buffer - Supplies a pointer to the data buffer area. + Count - The count of items to move. + +Return Value: + + None + + +{ + + READ_PORT_BUFFER_UCHAR(Port, Buffer, Count); + +} +--*/ +/*++ + +VOID +ScsiPortReadPortBufferUshort( + IN PUSHORT Port, + IN PUSHORT Buffer, + IN ULONG Count + ) + + +Routine Description: + + Read a buffer of unsigned shorts from the specified port address. + +Arguments: + + Port - Supplies a pointer to the port address. + Buffer - Supplies a pointer to the data buffer area. + Count - The count of items to move. + +Return Value: + + None + + +{ + + READ_PORT_BUFFER_USHORT(Port, Buffer, Count); + +} +--*/ +/*++ + +VOID +ScsiPortReadPortBufferUlong( + IN PULONG Port, + IN PULONG Buffer, + IN ULONG Count + ) + + +Routine Description: + + Read a buffer of unsigned longs from the specified port address. + +Arguments: + + Port - Supplies a pointer to the port address. + Buffer - Supplies a pointer to the data buffer area. + Count - The count of items to move. + +Return Value: + + None + + +{ + + READ_PORT_BUFFER_ULONG(Port, Buffer, Count); + +} +--*/ +/*++ + +VOID +ScsiPortWritePortBufferUchar( + IN PUCHAR Port, + IN PUCHAR Buffer, + IN ULONG Count + ) + + +Routine Description: + + Write a buffer of unsigned bytes from the specified port address. + +Arguments: + + Port - Supplies a pointer to the port address. + Buffer - Supplies a pointer to the data buffer area. + Count - The count of items to move. + +Return Value: + + None + + +{ + + WRITE_PORT_BUFFER_UCHAR(Port, Buffer, Count); + +} +--*/ +/*++ + +VOID +ScsiPortWritePortBufferUshort( + IN PUSHORT Port, + IN PUSHORT Buffer, + IN ULONG Count + ) + + +Routine Description: + + Write a buffer of unsigned shorts from the specified port address. + +Arguments: + + Port - Supplies a pointer to the port address. + Buffer - Supplies a pointer to the data buffer area. + Count - The count of items to move. + +Return Value: + + None + + +{ + + WRITE_PORT_BUFFER_USHORT(Port, Buffer, Count); + +} +--*/ +/*++ + +VOID +ScsiPortWritePortBufferUlong( + IN PULONG Port, + IN PULONG Buffer, + IN ULONG Count + ) + + +Routine Description: + + Write a buffer of unsigned longs from the specified port address. + +Arguments: + + Port - Supplies a pointer to the port address. + Buffer - Supplies a pointer to the data buffer area. + Count - The count of items to move. + +Return Value: + + None + + +{ + + WRITE_PORT_BUFFER_ULONG(Port, Buffer, Count); + +} + +--*/ diff --git a/private/ntos/fw/duobase/mips/scsiboot.h b/private/ntos/fw/duobase/mips/scsiboot.h new file mode 100644 index 000000000..f622a8e48 --- /dev/null +++ b/private/ntos/fw/duobase/mips/scsiboot.h @@ -0,0 +1,462 @@ +/*++ + +Copyright (c) 1990 Microsoft Corporation + +Module Name: + + scsiboot.h + +Abstract: + + This file defines the necessary structures, defines, and functions for + the common SCSI boot port driver. + +Author: + + Jeff Havens (jhavens) 28-Feb-1991 + Mike Glass + +Revision History: + +--*/ + +#include "ntddscsi.h" + +// +// SCSI Get Configuration Information +// +// LUN Information +// + +typedef struct _LUNINFO { + UCHAR PathId; + UCHAR TargetId; + UCHAR Lun; + BOOLEAN DeviceClaimed; + PVOID DeviceObject; + struct _LUNINFO *NextLunInfo; + UCHAR InquiryData[INQUIRYDATABUFFERSIZE]; +} LUNINFO, *PLUNINFO; + +typedef struct _SCSI_BUS_SCAN_DATA { + USHORT Length; + UCHAR InitiatorBusId; + UCHAR NumberOfLogicalUnits; + PLUNINFO LunInfoList; +} SCSI_BUS_SCAN_DATA, *PSCSI_BUS_SCAN_DATA; + +typedef struct _SCSI_CONFIGURATION_INFO { + UCHAR NumberOfBuses; + PSCSI_BUS_SCAN_DATA BusScanData[1]; +} SCSI_CONFIGURATION_INFO, *PSCSI_CONFIGURATION_INFO; + +#define MAXIMUM_RETRIES 1 + +// +// SCSI device timeout values in seconds +// + +#define SCSI_DISK_TIMEOUT 5 +#define SCSI_CDROM_TIMEOUT 10 +#define SCSI_TAPE_TIMEOUT 120 + +// +// Adapter object transfer information. +// + +typedef struct _ADAPTER_TRANSFER { + PSCSI_REQUEST_BLOCK Srb; + PVOID LogicalAddress; + ULONG Length; +}ADAPTER_TRANSFER, *PADAPTER_TRANSFER; + +typedef struct _SRB_SCATTER_GATHER { + ULONG PhysicalAddress; + ULONG Length; +}SRB_SCATTER_GATHER, *PSRB_SCATTER_GATHER; + +// +// Srb Structure plus extra storage for the port driver. +// + +#define IRP_STACK_SIZE 2 + +typedef struct _FULL_SCSI_REQUEST_BLOCK { + SCSI_REQUEST_BLOCK Srb; + PVOID PreviousIrp; + IRP Irp; + IO_STACK_LOCATION IrpStack[IRP_STACK_SIZE]; + ULONG SrbExtensionSize; + MDL Mdl; + ULONG PageFrame[20]; +}FULL_SCSI_REQUEST_BLOCK, *PFULL_SCSI_REQUEST_BLOCK; + +// +// Logical unit extension +// + +typedef struct _LOGICAL_UNIT_EXTENSION { + UCHAR PathId; + UCHAR TargetId; + UCHAR Lun; + ULONG Flags; + PIRP CurrentRequest; + KSPIN_LOCK CurrentRequestSpinLock; + PVOID SpecificLuExtension; + struct _LOGICAL_UNIT_EXTENSION *NextLogicalUnit; + KDEVICE_QUEUE RequestQueue; + KSPIN_LOCK RequestQueueSpinLock; + LONG RequestTimeoutCounter; + ULONG RetryCount; + UCHAR NumberOfLogicalUnits; + PVOID MapRegisterBase; + ULONG NumberOfMapRegisters; + SRB_SCATTER_GATHER ScatterGather[17]; +} LOGICAL_UNIT_EXTENSION, *PLOGICAL_UNIT_EXTENSION; + +// +// Device extension +// + +typedef struct _DEVICE_EXTENSION { + + PDEVICE_OBJECT DeviceObject; + + // + // Dma Adapter information. + // + + PVOID MapRegisterBase; + PADAPTER_OBJECT DmaAdapterObject; + ADAPTER_TRANSFER FlushAdapterParameters; + + // + // Number of SCSI buses + // + + UCHAR NumberOfBuses; + + // + // SCSI Capabilities structure + // + + IO_SCSI_CAPABILITIES Capabilities; + + // + // SCSI port driver flags + // + + ULONG Flags; + + // + // SCSI port interrupt flags + // + + ULONG InterruptFlags; + + // + // List head for singlely linked list of complete IRPs. + // + + PIRP CompletedRequests; + + // + // Adapter object transfer parameters. + // + + ADAPTER_TRANSFER MapTransferParameters; + + KSPIN_LOCK SpinLock; + + // + // Miniport Initialization Routine + // + + PHW_INITIALIZE HwInitialize; + + // + // Miniport Start IO Routine + // + + PHW_STARTIO HwStartIo; + + // + // Miniport Interrupt Service Routine + // + + PHW_INTERRUPT HwInterrupt; + + // + // Miniport Reset Routine + // + + PHW_RESET_BUS HwReset; + + // + // Miniport DMA started Routine + // + + PHW_DMA_STARTED HwDmaStarted; + + // + // Buffers must be mapped into system space. + // + + BOOLEAN MapBuffers; + + // + // Is this device a bus master and does it require map registers. + // + + BOOLEAN MasterWithAdapter; + // + // Device extension for miniport routines. + // + + PVOID HwDeviceExtension; + + // + // Miniport request interrupt enabled/disable routine. + // + + PHW_INTERRUPT HwRequestInterrupt; + + // + // Miniport timer request routine. + // + + PHW_INTERRUPT HwTimerRequest; + + // + // SCSI configuration information from inquiries. + // + + PSCSI_CONFIGURATION_INFO ScsiInfo; + + // + // Miniport noncached device extension + // + + PVOID NonCachedExtension; + + // + // SrbExtension Zone Pool + // + + PVOID SrbExtensionZonePool; + PCHAR SrbExtensionPointer; + + // + // Physical address of zone pool + // + + ULONG PhysicalZoneBase; + + // + // Size of Srb extension. + // + + ULONG SrbExtensionSize; + + // + // Spinlock for zoned hash table entries + // + + KSPIN_LOCK ZoneSpinLock; + + // + // Logical Unit Extension + // + + ULONG HwLogicalUnitExtensionSize; + + PLOGICAL_UNIT_EXTENSION LogicalUnitList; + + ULONG TimerValue; + +} DEVICE_EXTENSION, *PDEVICE_EXTENSION; + +#define DEVICE_EXTENSION_SIZE sizeof(DEVICE_EXTENSION) + +// +// Port driver extension flags. +// + +#define PD_CURRENT_IRP_VALID 0X0001 +#define PD_RESET_DETECTED 0X0002 +#define PD_NOTIFICATION_IN_PROGRESS 0X0004 +#define PD_READY_FOR_NEXT_REQUEST 0X0008 +#define PD_FLUSH_ADAPTER_BUFFERS 0X0010 +#define PD_MAP_TRANSFER 0X0020 +#define PD_DISABLE_CALL_REQUEST 0X02000 +#define PD_DISABLE_INTERRUPTS 0X04000 +#define PD_ENABLE_CALL_REQUEST 0X08000 +#define PD_TIMER_CALL_REQUEST 0X10000 + +// +// Logical unit extension flags. +// + +#define PD_QUEUE_FROZEN 0X0001 +#define PD_LOGICAL_UNIT_IS_ACTIVE 0X0002 +#define PD_CURRENT_REQUEST_COMPLETE 0X0004 +#define PD_LOGICAL_UNIT_IS_BUSY 0X0008 + +// +// The timer interval for the miniport timer routine specified in +// units of 100 nanoseconds. +// +#define PD_TIMER_INTERVAL (250 * 1000 * 10) // 250 ms + +// +// The define the interloop stall. +// + +#define PD_INTERLOOP_STALL 5 + +#define MINIMUM_SRB_EXTENSIONS 8 +#define COMPLETION_DELAY 5 + +// +// Port driver error logging +// + +#define ERROR_LOG_ENTRY_LENGTH 8 + +typedef struct _ERROR_LOG_ENTRY { + UCHAR PathId; + UCHAR TargetId; + UCHAR Lun; + ULONG ErrorCode; + ULONG UniqueId; +} ERROR_LOG_ENTRY, *PERROR_LOG_ENTRY; + + +// +// Define global data structures +// + +extern ULONG ScsiPortCount; +extern FULL_SCSI_REQUEST_BLOCK PrimarySrb; +extern PDEVICE_OBJECT ScsiPortDeviceObject[10]; +extern PREAD_CAPACITY_DATA ReadCapacityBuffer; +extern PUCHAR SenseInfoBuffer; + +// +// Support routine. +// + +PIRP +InitializeIrp( + PFULL_SCSI_REQUEST_BLOCK FullSrb, + CCHAR MajorFunction, + PVOID DeviceObject, + PVOID BufferPointer, + ULONG BufferSize + ); + + +ARC_STATUS +GetAdapterCapabilities( + IN PDEVICE_OBJECT PortDeviceObject, + OUT PIO_SCSI_CAPABILITIES *PortCapabilities + ); + +ARC_STATUS +GetInquiryData( + IN PDEVICE_OBJECT PortDeviceObject, + IN PSCSI_CONFIGURATION_INFO *ConfigInfo + ); + +ARC_STATUS +ReadDriveCapacity( + IN PPARTITION_CONTEXT PartitionContext + ); + +ARC_STATUS +ScsiClassIoComplete( + IN PPARTITION_CONTEXT PartitionContext, + IN PIRP Irp, + IN PVOID Context + ); + +ARC_STATUS +SendSrbSynchronous( + PPARTITION_CONTEXT PartitionContext, + PSCSI_REQUEST_BLOCK Srb, + PVOID BufferAddress, + ULONG BufferLength, + BOOLEAN WriteToDevice + ); + +BOOLEAN +InterpretSenseInfo( + IN PSCSI_REQUEST_BLOCK Srb, + OUT ARC_STATUS *Status, + PPARTITION_CONTEXT PartitionContext + ); + +VOID +RetryRequest( + PPARTITION_CONTEXT PartitionContext, + PIRP Irp + ); + +PIRP +BuildRequest( + IN PPARTITION_CONTEXT PartitionContext, + IN PMDL Mdl, + IN ULONG LogicalBlockAddress, + IN BOOLEAN Operation + ); + + +// +// Define the necessary functions to simulate the I/O environment. +// + +#if defined(MIPS) || defined(ALPHA) +#define ExAllocatePool(Type, Size) FwAllocatePool(Size) +//#define DbgPrint FwPrint +#else +#define ExAllocatePool(Type, Size) FwAllocateHeap(Size) +#define DbgPrint BlPrint +#define PAUSE while (!GET_KEY()); + +typedef struct _DRIVER_LOOKUP_ENTRY { + PCHAR DevicePath; + PBL_DEVICE_ENTRY_TABLE DispatchTable; +} DRIVER_LOOKUP_ENTRY, *PDRIVER_LOOKUP_ENTRY; +#undef ASSERT +#define ASSERT( exp ) { \ + if (!(#exp)) { \ + BlPrint("ASSERT File: %s line: %lx\n", __FILE__, __LINE__); \ + PAUSE; \ + } \ +} + +VOID +ScsiPortExecute( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +#endif +#define ExFreePool(Size) +#define IoCallDriver(DeviceObject, Irp) ( \ + DeviceObject->CurrentIrp = Irp, \ + Irp->Tail.Overlay.CurrentStackLocation--, \ + ScsiPortExecute(DeviceObject, Irp), \ + Irp->Tail.Overlay.CurrentStackLocation++ ) +#define IoCompleteRequest(Irp, Boost) Irp->PendingReturned = FALSE +#define IoAllocateErrorLogEntry(DeviceObject, Length) NULL +#define IoWriteErrorLogEntry(Entry) +#define KeAcquireSpinLock(Lock, Irql) +#define KeReleaseSpinLock(Lock, Irql) +#define KiAcquireSpinLock(Lock) +#define KiReleaseSpinLock(Lock) +#define KeSynchronizeExecution(InterruptObject, ExecutionRoutine, Context) \ + (ExecutionRoutine)(Context) + +#define KeRaiseIrql(NewLevel, OldLevel) +#define KeLowerIrql(Level) diff --git a/private/ntos/fw/duobase/mips/scsidisk.c b/private/ntos/fw/duobase/mips/scsidisk.c new file mode 100644 index 000000000..9c9579654 --- /dev/null +++ b/private/ntos/fw/duobase/mips/scsidisk.c @@ -0,0 +1,2037 @@ +/*++ + +Copyright (c) 1990 Microsoft Corporation + +Module Name: + + scsiflop.c + +Abstract: + + This module implements the scsi floppy disk boot driver for the DUO Base prom. + +Author: + + Lluis Abello (lluis) Apr-15-93. + All code stolen from bldr\scsidisk.c by jhavens. Disk Partitions, CDROMS and + so on removed. + +Environment: + + Kernel mode + +Revision History: + +--*/ + + +#include "fwp.h" +#include "ntdddisk.h" +#include "scsi.h" +#include "scsiboot.h" +#include "stdio.h" +#include "string.h" +#include "duobase.h" + +// +// SCSI driver constants. +// + +#define MAXIMUM_NUMBER_SECTORS 128 // maximum number of transfer sector +#define MAXIMUM_NUMBER_RETRIES 8 // maximum number of read/write retries +#define MAXIMUM_SECTOR_SIZE 2048 // define the maximum supported sector size +#define MODE_DATA_SIZE 192 +#define HITACHI_MODE_DATA_SIZE 8 + +// +// Define device driver prototypes. +// + +ARC_STATUS +ScsiDiskClose ( + IN ULONG FileId + ); + +ARC_STATUS +ScsiDiskMount ( + IN PCHAR MountPath, + IN MOUNT_OPERATION Operation + ); + +ARC_STATUS +ScsiDiskOpen ( + IN PCHAR OpenPath, + IN OPEN_MODE OpenMode, + OUT PULONG FileId + ); + +ARC_STATUS +ScsiDiskRead ( + IN ULONG FileId, + IN PVOID Buffer, + IN ULONG Length, + OUT PULONG Count + ); + +ARC_STATUS +ScsiDiskGetReadStatus ( + IN ULONG FileId + ); + +ARC_STATUS +ScsiDiskSeek ( + IN ULONG FileId, + IN PLARGE_INTEGER Offset, + IN SEEK_MODE SeekMode + ); + + +ARC_STATUS +ScsiDiskGetFileInformation ( + IN ULONG FileId, + OUT PFILE_INFORMATION Finfo + ); + +NTSTATUS +ScsiDiskBootIO ( + IN PMDL MdlAddress, + IN ULONG LogicalBlock, + IN PPARTITION_CONTEXT PartitionContext + ); + +VOID +ScsiDiskBootSetup ( + VOID + ); + +VOID +ScsiPortExecute( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +VOID +ScsiDiskStartUnit( + IN PPARTITION_CONTEXT PartitionContext + ); + +VOID +ScsiDiskStartUnit( + IN PPARTITION_CONTEXT PartitionContext + ); + +ULONG +ClassModeSense( + IN PPARTITION_CONTEXT Context, + IN PCHAR ModeSenseBuffer, + IN ULONG Length, + IN UCHAR PageMode + ); + +PVOID +ClassFindModePage( + IN PCHAR ModeSenseBuffer, + IN ULONG Length, + IN UCHAR PageMode + ); +BOOLEAN +IsFloppyDevice( + PPARTITION_CONTEXT Context + ); + +PIRP +BuildReadRequest( + IN PPARTITION_CONTEXT PartitionContext, + IN PMDL Mdl, + IN ULONG LogicalBlockAddress + ); + + + +// +// Define static data. +// + +BL_DEVICE_ENTRY_TABLE ScsiDiskEntryTable = { + ScsiDiskClose, + NULL, + ScsiDiskOpen, + ScsiDiskRead, + ScsiDiskGetReadStatus, + ScsiDiskSeek, + NULL, + ScsiDiskGetFileInformation, + (PARC_SET_FILE_INFO_ROUTINE)NULL + }; + + +// +// Global poiter for buffers. +// + +PREAD_CAPACITY_DATA ReadCapacityBuffer; +PUCHAR SenseInfoBuffer; + +#define SECTORS_IN_LOGICAL_VOLUME 0x20 + + +ARC_STATUS +ScsiDiskGetFileInformation ( + IN ULONG FileId, + OUT PFILE_INFORMATION Finfo + ) + +/*++ + +Routine Description: + + This routine returns information on the scsi partition. + +Arguments: + + FileId - Supplies the file table index. + + Finfo - Supplies a pointer to where the File Informatino is stored. + +Return Value: + + ESUCCESS is returned. + +--*/ + +{ + + PPARTITION_CONTEXT Context; + + RtlZeroMemory(Finfo, sizeof(FILE_INFORMATION)); + + Context = &BlFileTable[FileId].u.PartitionContext; + + Finfo->StartingAddress = RtlConvertLongToLargeInteger (Context->StartingSector); + Finfo->StartingAddress = RtlLargeIntegerShiftLeft(Finfo->StartingAddress, + Context->SectorShift); + + Finfo->EndingAddress = RtlLargeIntegerAdd(Finfo->StartingAddress, + Context->PartitionLength); + + Finfo->Type = DiskPeripheral; + + return ESUCCESS; +} + + +ARC_STATUS +ScsiDiskClose ( + IN ULONG FileId + ) + +/*++ + +Routine Description: + + This function closes the file table entry specified by the file id. + +Arguments: + + FileId - Supplies the file table index. + +Return Value: + + ESUCCESS is returned. + +--*/ + +{ + + BlFileTable[FileId].Flags.Open = 0; + return ESUCCESS; +} + +ARC_STATUS +ScsiDiskOpen ( + IN PCHAR OpenPath, + IN OPEN_MODE OpenMode, + OUT PULONG FileId + ) + +/*++ + +Routine Description: + + This routine fills in the file table entry. In particular the Scsi address + of the device is determined from the name. The block size of device is + queried from the target controller, and the partition information is read + from the device. + +Arguments: + + OpenPath - Supplies the name of the device being opened. + + OpenMode - Unused. + + FileId - Supplies the index to the file table entry to be initialized. + +Return Value: + + Retruns the arc status of the operation. + +--*/ + +{ + ULONG Partition; + ULONG Id; + BOOLEAN IsCdRom; + BOOLEAN IsFloppy; + PPARTITION_CONTEXT Context; + + Context = &BlFileTable[*FileId].u.PartitionContext; + + // + // Determine the scsi port device object. + // + + if (FwGetPathMnemonicKey(OpenPath, "scsi", &Id)) { + return ENODEV; + } + + if (ScsiPortDeviceObject[Id] == NULL) { + return ENODEV; + } + + Context->PortDeviceObject = ScsiPortDeviceObject[Id]; + + // + // Get the logical unit, path Id and target id from the name. + // If it's not a floppy return ENODEV. + // NOTE: FwGetPathMnemonicKey returns 0 for success. + // + + if (FwGetPathMnemonicKey(OpenPath, "fdisk", &Id)) { + return ENODEV; + } + + Context->DiskId = Id; + + if (FwGetPathMnemonicKey(OpenPath, "disk", &Id)) { + return ENODEV; + } + + Context->PathId = Id / SCSI_MAXIMUM_TARGETS_PER_BUS; + + Context->TargetId = Id % SCSI_MAXIMUM_TARGETS_PER_BUS; + + // + // Read the capacity of the disk to determine the block size. + // + + if (ReadDriveCapacity(Context)) { + return ENODEV; + } + return ESUCCESS; +} + +ARC_STATUS +ScsiDiskRead ( + IN ULONG FileId, + IN PVOID Buffer, + IN ULONG Length, + OUT PULONG Count + ) + +/*++ + +Routine Description: + + This function reads data from the hard disk starting at the position + specified in the file table. + + +Arguments: + + FileId - Supplies the file table index. + + Buffer - Supplies a poiner to the buffer that receives the data + read. + + Length - Supplies the number of bytes to be read. + + Count - Supplies a pointer to a variable that receives the number of + bytes actually read. + +Return Value: + + The read operation is performed and the read completion status is + returned. + +--*/ + + +{ + + ARC_STATUS ArcStatus; + ULONG FrameNumber; + ULONG Index; + ULONG Limit; + PMDL MdlAddress; + UCHAR MdlBuffer[sizeof(MDL) + ((64 / 4) + 1) * sizeof(ULONG)]; + NTSTATUS NtStatus; + ULONG NumberOfPages; + PULONG PageFrame; + ULONG Offset; + LARGE_INTEGER Position; + LARGE_INTEGER LogicalBlock; + CHAR TempBuffer[MAXIMUM_SECTOR_SIZE + 128]; + PCHAR TempPointer; + PIO_SCSI_CAPABILITIES PortCapabilities; + ULONG adapterLimit; + ULONG SectorSize; + ULONG TransferCount; + ULONG BytesToTransfer; + + // + // If the requested size of the transfer is zero return ESUCCESS + // + + if (Length==0) { + return ESUCCESS; + } + + // + // Compute a Dcache aligned pointer into the temporary buffer. + // + + TempPointer = (PVOID)((ULONG)(TempBuffer + + KeGetDcacheFillSize() - 1) & ~(KeGetDcacheFillSize() - 1)); + + + // + // Calculate the actual sector size. + // + + SectorSize = 1 << BlFileTable[FileId].u.PartitionContext.SectorShift; + + // + // If the current position is not at a sector boundary, then read the + // first sector separately and copy the data. + // + + Offset = BlFileTable[FileId].Position.LowPart & (SectorSize - 1); + *Count = 0; + if (Offset != 0) { + + Position = BlFileTable[FileId].Position; + BlFileTable[FileId].Position = RtlLargeIntegerSubtract(Position, + RtlConvertLongToLargeInteger(Offset)); + ArcStatus = ScsiDiskRead(FileId, TempPointer, SectorSize, &TransferCount); + if (ArcStatus != ESUCCESS) { + BlFileTable[FileId].Position = Position; + return ArcStatus; + } + + // + // Copy the data to the specified buffer. + // + + if ((SectorSize - Offset) > Length) { + Limit = Offset + Length; + + } else { + Limit = SectorSize; + } + + for (Index = Offset; Index < Limit; Index += 1) { + ((PCHAR)Buffer)[Index - Offset] = TempPointer[Index]; + } + + // + // Update transfer parameters. + // + + *Count += Limit - Offset; + Length -= Limit - Offset; + Buffer = (PVOID)((PCHAR)Buffer + Limit - Offset); + BlFileTable[FileId].Position = RtlLargeIntegerAdd( Position, + RtlConvertLongToLargeInteger(Limit - Offset)); + } + + ArcStatus = GetAdapterCapabilities( + BlFileTable[FileId].u.PartitionContext.PortDeviceObject, + &PortCapabilities + ); + + if (ArcStatus != ESUCCESS || + PortCapabilities->MaximumTransferLength < 0x1000 || + PortCapabilities->MaximumTransferLength > 0x10000) { + + adapterLimit = 0x10000; + + } else { + + adapterLimit = PortCapabilities->MaximumTransferLength; + } + + // + // The position is aligned on a sector boundary. Read as many sectors + // as possible in a contiguous run in 64Kb chunks. + // + + BytesToTransfer = Length & (~(SectorSize - 1)); + while (BytesToTransfer != 0) { + + // + // The scsi controller doesn't support transfers bigger than 64Kb. + // Transfer the maximum number of bytes possible. + // + + Limit = (BytesToTransfer > adapterLimit ? adapterLimit : BytesToTransfer); + + // + // Build the memory descriptor list. + // + + + MdlAddress = (PMDL)&MdlBuffer[0]; + MdlAddress->Next = NULL; + MdlAddress->Size = sizeof(MDL) + + ADDRESS_AND_SIZE_TO_SPAN_PAGES(Buffer, Limit) * sizeof(ULONG); + MdlAddress->StartVa = (PVOID)PAGE_ALIGN(Buffer); + MdlAddress->ByteCount = Limit; + MdlAddress->ByteOffset = BYTE_OFFSET(Buffer); + PageFrame = (PULONG)(MdlAddress + 1); + FrameNumber = (((ULONG)MdlAddress->StartVa) & 0x1fffffff) >> PAGE_SHIFT; + NumberOfPages = (MdlAddress->ByteCount + + MdlAddress->ByteOffset + PAGE_SIZE - 1) >> PAGE_SHIFT; + for (Index = 0; Index < NumberOfPages; Index += 1) { + *PageFrame++ = FrameNumber++; + } + + // + // Flush I/O buffers and read from the boot device. + // + + KeFlushIoBuffers(MdlAddress, TRUE, TRUE); + LogicalBlock = RtlLargeIntegerShiftRight(BlFileTable[FileId].Position, + BlFileTable[FileId].u.PartitionContext.SectorShift); + LogicalBlock.LowPart += BlFileTable[FileId].u.PartitionContext.StartingSector; + NtStatus = ScsiDiskBootIO(MdlAddress, + LogicalBlock.LowPart, + &BlFileTable[FileId].u.PartitionContext); + + if (NtStatus != ESUCCESS) { + return EIO; + } + + *Count += Limit; + Length -= Limit; + Buffer = (PVOID)((PCHAR)Buffer + Limit); + BytesToTransfer -= Limit; + BlFileTable[FileId].Position = RtlLargeIntegerAdd(BlFileTable[FileId].Position, + RtlConvertLongToLargeInteger(Limit)); + } + + // + // If there is any residual data to read, then read the last sector + // separately and copy the data. + // + + if (Length != 0) { + Position = BlFileTable[FileId].Position; + ArcStatus = ScsiDiskRead(FileId, TempPointer, SectorSize, &TransferCount); + if (ArcStatus != ESUCCESS) { + BlFileTable[FileId].Position = Position; + return ArcStatus; + } + + // + // Copy the data to the specified buffer. + // + + for (Index = 0; Index < Length; Index += 1) { + ((PCHAR)Buffer)[Index] = TempPointer[Index]; + } + + // + // Update transfer parameters. + // + + *Count += Length; + BlFileTable[FileId].Position = RtlLargeIntegerAdd(Position, + RtlConvertLongToLargeInteger(Length)); + } + + return ESUCCESS; + +} + +ARC_STATUS +ScsiDiskGetReadStatus ( + IN ULONG FileId + ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ + +{ + return ESUCCESS; +} + +ARC_STATUS +ScsiDiskSeek ( + IN ULONG FileId, + IN PLARGE_INTEGER Offset, + IN SEEK_MODE SeekMode + ) + +/*++ + +Routine Description: + + This function sets the device position to the specified offset for + the specified file id. + +Arguments: + + FileId - Supplies the file table index. + + Offset - Supplies to new device position. + + SeekMode - Supplies the mode for the position. + +Return Value: + + ESUCCESS is returned. + +--*/ + +{ + + // + // Set the current device position as specifed by the seek mode. + // + + if (SeekMode == SeekAbsolute) { + BlFileTable[FileId].Position = *Offset; + + } else if (SeekMode == SeekRelative) { + BlFileTable[FileId].Position = RtlLargeIntegerAdd(BlFileTable[FileId].Position, + *Offset); + } + + return ESUCCESS; +} + +VOID +HardDiskInitialize( + IN OUT PDRIVER_LOOKUP_ENTRY LookupTable, + IN ULONG Entries + ) + +/*++ + +Routine Description: + + This routine initializes the scsi controller and the + device entry table for the scsi driver. + +Arguments: + + LookupTable. + Entries + +Return Value: + + None. + +--*/ + +{ + ULONG scsiNumber; + PDEVICE_EXTENSION scsiPort; + PSCSI_CONFIGURATION_INFO configInfo; + PSCSI_BUS_SCAN_DATA busScanData; + ULONG busNumber; + PLUNINFO lunInfo; + PINQUIRYDATA inquiryData; + PCHAR Identifier; + PARTITION_CONTEXT Context; + + RtlZeroMemory(&Context, sizeof(PARTITION_CONTEXT)); + + // + // Initialize the common buffers. + // + + ReadCapacityBuffer = ExAllocatePool( NonPagedPool, sizeof(READ_CAPACITY_DATA)); + + SenseInfoBuffer = ExAllocatePool( NonPagedPool, SENSE_BUFFER_SIZE); + + if (ReadCapacityBuffer == NULL || SenseInfoBuffer == NULL) { + return; + } + + // + // Scan the scsi ports looking for floppy disk devices. + // + + for (scsiNumber = 0; ScsiPortDeviceObject[scsiNumber]; scsiNumber++) { + + scsiPort = ScsiPortDeviceObject[scsiNumber]->DeviceExtension; + configInfo = scsiPort->ScsiInfo; + Context.PortDeviceObject = ScsiPortDeviceObject[scsiNumber]; + + for (busNumber=0; busNumber < (ULONG)configInfo->NumberOfBuses; busNumber++) { + + busScanData = configInfo->BusScanData[busNumber]; + + // + // Set LunInfo to beginning of list. + // + + lunInfo = busScanData->LunInfoList; + + while (lunInfo != NULL) { + + inquiryData = (PVOID)lunInfo->InquiryData; + + ScsiDebugPrint(3,"FindScsiDevices: Inquiry data at %lx\n", + inquiryData); + + if ((inquiryData->DeviceType == DIRECT_ACCESS_DEVICE) && + (!lunInfo->DeviceClaimed)) { + //DbgPrint("ScsiVendor ID at %lx\n",inquiryData->VendorId); + //DbgBreakPoint(); + + ScsiDebugPrint(1, + "FindScsiDevices: Vendor string is %.24s\n", + inquiryData->VendorId); + + // + // Create a dummy paritition context so that I/O can be + // done on the device. SendSrbSynchronous only uses the + // port device object pointer and the scsi address of the + // logical unit. + // + + Context.PathId = lunInfo->PathId; + Context.TargetId = lunInfo->TargetId; + Context.DiskId = lunInfo->Lun; + + // + // Check to see if the device is a floppy. + // + // + if (inquiryData->RemovableMedia && + inquiryData->DeviceType == DIRECT_ACCESS_DEVICE && + IsFloppyDevice(&Context) ) { + + // + // Create name for disk object. + // + + LookupTable->DevicePath = + ExAllocatePool(NonPagedPool, + sizeof("scsi(%d)disk(%d)fdisk(%d)")); + + if (LookupTable->DevicePath == NULL) { + return; + } + + sprintf(LookupTable->DevicePath, + "scsi(%d)disk(%d)fdisk(%d)", + scsiNumber, + lunInfo->TargetId + lunInfo->PathId * SCSI_MAXIMUM_TARGETS_PER_BUS, + lunInfo->Lun + ); + LookupTable->DispatchTable = &ScsiDiskEntryTable; + + ScsiDebugPrint(1,"Found ARC device %s\n",LookupTable->DevicePath); + // + // Increment to the next entry. + // + + LookupTable++; + + // + // Claim disk device by marking configuration + // record owned. + // + + lunInfo->DeviceClaimed = TRUE; + + } + } + + // + // Get next LunInfo. + // + + lunInfo = lunInfo->NextLunInfo; + } + } + } +} + + +NTSTATUS +ScsiDiskBootIO ( + IN PMDL MdlAddress, + IN ULONG LogicalBlock, + IN PPARTITION_CONTEXT PartitionContext + ) + +/*++ + +Routine Description: + + This routine is the read/write routine for the hard disk boot driver. + +Arguments: + + MdlAddress - Supplies a pointer to an MDL for the IO operation. + + LogicalBlock - Supplies the starting block number. + + DeviceUnit - Supplies the SCSI Id number. + +Return Value: + + The final status of the read operation (STATUS_UNSUCCESSFUL or + STATUS_SUCCESS). + +--*/ + +{ + ARC_STATUS Status; + PIRP Irp; + PIO_STACK_LOCATION NextIrpStack; + PSCSI_REQUEST_BLOCK Srb; + ULONG RetryCount = MAXIMUM_RETRIES; + + ScsiDebugPrint(1,"ScsiDiskBootIO enter routine\n"); + + // + // Check that the request is within the limits of the partition. + // + if (PartitionContext->StartingSector > LogicalBlock) { + return STATUS_UNSUCCESSFUL; + } + if (PartitionContext->EndingSector < + LogicalBlock + (MdlAddress->ByteCount >> PartitionContext->SectorShift)) { + return STATUS_UNSUCCESSFUL; + } + +Retry: + + // + // Build the I/O Request. + // + + Irp = BuildReadRequest(PartitionContext, MdlAddress, LogicalBlock); + + NextIrpStack = IoGetNextIrpStackLocation(Irp); + Srb = NextIrpStack->Parameters.Others.Argument1; + + // + // Call the port driver. + // + + IoCallDriver(PartitionContext->PortDeviceObject, Irp); + + // + // Check the status. + // + + if (SRB_STATUS(Srb->SrbStatus) != SRB_STATUS_SUCCESS) { + + // + // Determine the cause of the error. + // + + if (InterpretSenseInfo(Srb, &Status, PartitionContext) && RetryCount--) { + + goto Retry; + } + + if (Status == EAGAIN) { + Status = EIO; + } + + ScsiDebugPrint((1, "SCSI: Read request failed. Arc Status: %d, Srb Status: %x\n", + Status, + Srb->SrbStatus + )); + + } else { + + Status = ESUCCESS; + + } + + return(Status); +} + +ARC_STATUS +ReadDriveCapacity( + IN PPARTITION_CONTEXT PartitionContext + ) + +/*++ + +Routine Description: + + This routine sends a read capacity to a target id and returns + when it is complete. + +Arguments: + +Return Value: + + Status is returned. + +--*/ +{ + PCDB Cdb; + PSCSI_REQUEST_BLOCK Srb = &PrimarySrb.Srb; + ULONG LastSector; + ULONG retries = 1; + ARC_STATUS status; + ULONG BytesPerSector; + + ScsiDebugPrint(2,"SCSI ReadCapacity: Enter routine\n"); + + + // + // Build the read capacity CDB. + // + + Srb->CdbLength = 10; + Cdb = (PCDB)Srb->Cdb; + + // + // Zero CDB in SRB on stack. + // + + RtlZeroMemory(Cdb, MAXIMUM_CDB_SIZE); + + Cdb->CDB10.OperationCode = SCSIOP_READ_CAPACITY; + +Retry: + + status = SendSrbSynchronous(PartitionContext, + Srb, + ReadCapacityBuffer, + sizeof(READ_CAPACITY_DATA), + FALSE); + + if (status == ESUCCESS) { + + BytesPerSector = 0; + + // + // Copy sector size from read capacity buffer to device extension + // in reverse byte order. + // + + ((PFOUR_BYTE)&BytesPerSector)->Byte0 = + ((PFOUR_BYTE)&ReadCapacityBuffer->BytesPerBlock)->Byte3; + + ((PFOUR_BYTE)&BytesPerSector)->Byte1 = + ((PFOUR_BYTE)&ReadCapacityBuffer->BytesPerBlock)->Byte2; + + if (BytesPerSector == 0) { + + // + // Assume this is a brain dead cd-rom and the sector size is 2048. + // + + BytesPerSector = 2048; + + } + + // + // Calculate sector to byte shift. + // + + WHICH_BIT(BytesPerSector, PartitionContext->SectorShift); + + // + // Copy last sector in reverse byte order. + // + + ((PFOUR_BYTE)&LastSector)->Byte0 = + ((PFOUR_BYTE)&ReadCapacityBuffer->LogicalBlockAddress)->Byte3; + + ((PFOUR_BYTE)&LastSector)->Byte1 = + ((PFOUR_BYTE)&ReadCapacityBuffer->LogicalBlockAddress)->Byte2; + + ((PFOUR_BYTE)&LastSector)->Byte2 = + ((PFOUR_BYTE)&ReadCapacityBuffer->LogicalBlockAddress)->Byte1; + + ((PFOUR_BYTE)&LastSector)->Byte3 = + ((PFOUR_BYTE)&ReadCapacityBuffer->LogicalBlockAddress)->Byte0; + + + PartitionContext->PartitionLength = RtlConvertLongToLargeInteger(LastSector + 1); + PartitionContext->PartitionLength = RtlLargeIntegerShiftLeft(PartitionContext->PartitionLength, + PartitionContext->SectorShift); + + PartitionContext->StartingSector=0; + PartitionContext->EndingSector = LastSector + 1; + + ScsiDebugPrint(2,"SCSI ReadDriveCapacity: Sector size is %d\n", + BytesPerSector); + + ScsiDebugPrint(2,"SCSI ReadDriveCapacity: Number of Sectors is %d\n", + LastSector + 1); + + + } + + if (status == EAGAIN || status == EBUSY) { + + if (retries--) { + + // + // Retry request. + // + + goto Retry; + } + } + + return status; + +} // end ReadDriveCapacity() + + +ARC_STATUS +SendSrbSynchronous( + PPARTITION_CONTEXT PartitionContext, + PSCSI_REQUEST_BLOCK Srb, + PVOID BufferAddress, + ULONG BufferLength, + BOOLEAN WriteToDevice + ) + +/*++ + +Routine Description: + + This routine is called by SCSI device controls to complete an + SRB and send it to the port driver synchronously (ie wait for + completion). + The CDB is already completed along with the SRB CDB size and + request timeout value. + +Arguments: + + PartitionContext + SRB + Buffer address and length (if transfer) + + WriteToDevice - Indicates the direction of the transfer. + +Return Value: + + ARC_STATUS + +--*/ + +{ + PIRP Irp; + PIO_STACK_LOCATION IrpStack; + ULONG retryCount = 1; + ARC_STATUS status; + + // + // Write length to SRB. + // + + Srb->Length = SCSI_REQUEST_BLOCK_SIZE; + + // + // Set SCSI bus address. + // + + Srb->PathId = PartitionContext->PathId; + Srb->TargetId = PartitionContext->TargetId; + Srb->Lun = PartitionContext->DiskId; + + Srb->Function = SRB_FUNCTION_EXECUTE_SCSI; + + // + // Enable auto request sense. + // + + Srb->SenseInfoBufferLength = SENSE_BUFFER_SIZE; + + if (SenseInfoBuffer == NULL) { + ScsiDebugPrint(1,"SendSrbSynchronous: Can't allocate request sense buffer\n"); + return(ENOMEM); + } + + Srb->SenseInfoBuffer = SenseInfoBuffer; + + Srb->DataBuffer = BufferAddress; + + // + // Start retries here. + // + +retry: + + Irp = InitializeIrp( + &PrimarySrb, + IRP_MJ_SCSI, + PartitionContext->PortDeviceObject, + BufferAddress, + BufferLength + ); + + if (BufferAddress != NULL) { + + if (WriteToDevice) { + + Srb->SrbFlags = SRB_FLAGS_DATA_OUT; + + } else { + + Srb->SrbFlags = SRB_FLAGS_DATA_IN; + + } + + } else { + + // + // Clear flags. + // + + Srb->SrbFlags = SRB_FLAGS_NO_DATA_TRANSFER; + } + + // + // Disable synchronous transfers. + // + + Srb->SrbFlags |= SRB_FLAGS_DISABLE_SYNCH_TRANSFER; + + // + // Set the transfer length. + // + + Srb->DataTransferLength = BufferLength; + + // + // Zero out status. + // + + Srb->ScsiStatus = Srb->SrbStatus = 0; + + // + // Get next stack location and + // set major function code. + // + + IrpStack = IoGetNextIrpStackLocation(Irp); + + + // + // Set up SRB for execute scsi request. + // Save SRB address in next stack for port driver. + // + + IrpStack->Parameters.Others.Argument1 = (PVOID)Srb; + + // + // Set up IRP Address. + // + + Srb->OriginalRequest = Irp; + + Srb->NextSrb = 0; + + // + // No need to check the following 2 returned statuses as + // SRB will have ending status. + // + + (VOID)IoCallDriver(PartitionContext->PortDeviceObject, Irp); + + // + // Check that request completed without error. + // + + if (SRB_STATUS(Srb->SrbStatus) != SRB_STATUS_SUCCESS) { + + // + // Update status and determine if request should be retried. + // + + if (InterpretSenseInfo(Srb, &status, PartitionContext)) { + + // + // If retries are not exhausted then + // retry this operation. + // + + if (retryCount--) { + goto retry; + } + } + + } else { + + status = ESUCCESS; + } + + return status; + +} // end SendSrbSynchronous() + + +BOOLEAN +InterpretSenseInfo( + IN PSCSI_REQUEST_BLOCK Srb, + OUT ARC_STATUS *Status, + PPARTITION_CONTEXT PartitionContext + ) + +/*++ + +Routine Description: + + This routine interprets the data returned from the SCSI + request sense. It determines the status to return in the + IRP and whether this request can be retried. + +Arguments: + + DeviceObject + SRB + ARC_STATUS to update IRP + +Return Value: + + BOOLEAN TRUE: Drivers should retry this request. + FALSE: Drivers should not retry this request. + +--*/ + +{ + PSENSE_DATA SenseBuffer = Srb->SenseInfoBuffer; + BOOLEAN retry; + + // + // Check that request sense buffer is valid. + // + + if (Srb->SrbStatus & SRB_STATUS_AUTOSENSE_VALID) { + + ScsiDebugPrint(2,"InterpretSenseInfo: Error code is %x\n", + SenseBuffer->ErrorCode); + + ScsiDebugPrint(2,"InterpretSenseInfo: Sense key is %x\n", + SenseBuffer->SenseKey); + + ScsiDebugPrint(2,"InterpretSenseInfo: Additional sense code is %x\n", + SenseBuffer->AdditionalSenseCode); + + ScsiDebugPrint(2,"InterpretSenseInfo: Additional sense code qualifier is %x\n", + SenseBuffer->AdditionalSenseCodeQualifier); + + switch (SenseBuffer->SenseKey) { + + case SCSI_SENSE_NOT_READY: + + ScsiDebugPrint(1,"InterpretSenseInfo: Device not ready\n"); + + ScsiDebugPrint(1,"InterpretSenseInfo: Waiting for device\n"); + + *Status = EBUSY; + + retry = TRUE; + + switch (SenseBuffer->AdditionalSenseCode) { + + case SCSI_ADSENSE_LUN_NOT_READY: + + ScsiDebugPrint(1,"InterpretSenseInfo: Lun not ready\n"); + + switch (SenseBuffer->AdditionalSenseCodeQualifier) { + + case SCSI_SENSEQ_BECOMING_READY: + + ScsiDebugPrint(1, + "InterpretSenseInfo:" + " In process of becoming ready\n"); + + FwStallExecution( 1000 * 1000 * 3 ); + + break; + + case SCSI_SENSEQ_MANUAL_INTERVENTION_REQUIRED: + + ScsiDebugPrint(1, + "InterpretSenseInfo:" + " Manual intervention required\n"); + *Status = STATUS_NO_MEDIA_IN_DEVICE; + retry = FALSE; + break; + + case SCSI_SENSEQ_FORMAT_IN_PROGRESS: + + ScsiDebugPrint(1, + "InterpretSenseInfo:" + " Format in progress\n"); + retry = FALSE; + break; + + default: + + FwStallExecution( 1000 * 1000 * 3 ); + + // + // Try a start unit too. + // + + case SCSI_SENSEQ_INIT_COMMAND_REQUIRED: + + ScsiDebugPrint(1, + "InterpretSenseInfo:" + " Initializing command required\n"); + + // + // This sense code/additional sense code + // combination may indicate that the device + // needs to be started. + // + + ScsiDiskStartUnit(PartitionContext); + break; + + } + + } // end switch + + break; + + case SCSI_SENSE_DATA_PROTECT: + + ScsiDebugPrint(1,"InterpretSenseInfo: Media write protected\n"); + + *Status = EACCES; + + retry = FALSE; + + break; + + case SCSI_SENSE_MEDIUM_ERROR: + + ScsiDebugPrint(1,"InterpretSenseInfo: Bad media\n"); + *Status = EIO; + + retry = TRUE; + + break; + + case SCSI_SENSE_HARDWARE_ERROR: + + ScsiDebugPrint(1,"InterpretSenseInfo: Hardware error\n"); + *Status = EIO; + + retry = TRUE; + + break; + + case SCSI_SENSE_ILLEGAL_REQUEST: + + ScsiDebugPrint(1,"InterpretSenseInfo: Illegal SCSI request\n"); + + switch (SenseBuffer->AdditionalSenseCode) { + + case SCSI_ADSENSE_ILLEGAL_COMMAND: + ScsiDebugPrint(1,"InterpretSenseInfo: Illegal command\n"); + break; + + case SCSI_ADSENSE_ILLEGAL_BLOCK: + ScsiDebugPrint(1,"InterpretSenseInfo: Illegal block address\n"); + break; + + case SCSI_ADSENSE_INVALID_LUN: + ScsiDebugPrint(1,"InterpretSenseInfo: Invalid LUN\n"); + break; + + case SCSI_ADSENSE_MUSIC_AREA: + ScsiDebugPrint(1,"InterpretSenseInfo: Music area\n"); + break; + + case SCSI_ADSENSE_DATA_AREA: + ScsiDebugPrint(1,"InterpretSenseInfo: Data area\n"); + break; + + case SCSI_ADSENSE_VOLUME_OVERFLOW: + ScsiDebugPrint(1,"InterpretSenseInfo: Volume overflow\n"); + break; + + } // end switch ... + + *Status = EINVAL; + + retry = FALSE; + + break; + + case SCSI_SENSE_UNIT_ATTENTION: + + ScsiDebugPrint(3,"InterpretSenseInfo: Unit attention\n"); + + switch (SenseBuffer->AdditionalSenseCode) { + + case SCSI_ADSENSE_MEDIUM_CHANGED: + ScsiDebugPrint(1,"InterpretSenseInfo: Media changed\n"); + break; + + case SCSI_ADSENSE_BUS_RESET: + break; + ScsiDebugPrint(1,"InterpretSenseInfo: Bus reset\n"); + + } + + *Status = EAGAIN; + + retry = TRUE; + + break; + + case SCSI_SENSE_ABORTED_COMMAND: + + ScsiDebugPrint(1,"InterpretSenseInfo: Command aborted\n"); + + *Status = EIO; + + retry = TRUE; + + break; + + case SCSI_SENSE_NO_SENSE: + + ScsiDebugPrint(1,"InterpretSenseInfo: No specific sense key\n"); + + *Status = EIO; + + retry = TRUE; + + break; + + default: + + ScsiDebugPrint(1,"InterpretSenseInfo: Unrecognized sense code\n"); + + *Status = STATUS_UNSUCCESSFUL; + + retry = TRUE; + + } // end switch + + } else { + + // + // Request sense buffer not valid. No sense information + // to pinpoint the error. Return general request fail. + // + + ScsiDebugPrint(1,"InterpretSenseInfo: Request sense info not valid\n"); + + *Status = EIO; + + retry = TRUE; + } + + return retry; + +} // end InterpretSenseInfo() + + +VOID +RetryRequest( + PPARTITION_CONTEXT PartitionContext, + PIRP Irp + ) + +/*++ + +Routine Description: + +Arguments: + +Return Value: + +--*/ + +{ + PIO_STACK_LOCATION NextIrpStack = IoGetNextIrpStackLocation(Irp); + PSCSI_REQUEST_BLOCK Srb = &PrimarySrb.Srb; + PMDL Mdl = Irp->MdlAddress; + ULONG TransferByteCount = Mdl->ByteCount; + + + // + // Reset byte count of transfer in SRB Extension. + // + + Srb->DataTransferLength = TransferByteCount; + + // + // Zero SRB statuses. + // + + Srb->SrbStatus = Srb->ScsiStatus = 0; + + // + // Set up major SCSI function. + // + + NextIrpStack->MajorFunction = IRP_MJ_SCSI; + + // + // Save SRB address in next stack for port driver. + // + + NextIrpStack->Parameters.Others.Argument1 = (PVOID)Srb; + + // + // Return the results of the call to the port driver. + // + + (PVOID)IoCallDriver(PartitionContext->PortDeviceObject, Irp); + + return; + +} // end RetryRequest() + +PIRP +BuildReadRequest( + IN PPARTITION_CONTEXT PartitionContext, + IN PMDL Mdl, + IN ULONG LogicalBlockAddress + ) + +/*++ + +Routine Description: + +Arguments: + +Note: + +If the IRP is for a disk transfer, the byteoffset field +will already have been adjusted to make it relative to +the beginning of the disk. In this way, this routine can +be shared between the disk and cdrom class drivers. + + +Return Value: + +--*/ + +{ + PIRP Irp = &PrimarySrb.Irp; + PIO_STACK_LOCATION NextIrpStack; + PSCSI_REQUEST_BLOCK Srb = &PrimarySrb.Srb; + PCDB Cdb; + USHORT TransferBlocks; + + // + // Initialize the rest of the IRP. + // + + Irp->MdlAddress = Mdl; + + Irp->Tail.Overlay.CurrentStackLocation = &PrimarySrb.IrpStack[IRP_STACK_SIZE]; + + NextIrpStack = IoGetNextIrpStackLocation(Irp); + + // + // Write length to SRB. + // + + Srb->Length = SCSI_REQUEST_BLOCK_SIZE; + + // + // Set up IRP Address. + // + + Srb->OriginalRequest = Irp; + + Srb->NextSrb = 0; + + // + // Set up target id and logical unit number. + // + + Srb->PathId = PartitionContext->PathId; + Srb->TargetId = PartitionContext->TargetId; + Srb->Lun = PartitionContext->DiskId; + + Srb->Function = SRB_FUNCTION_EXECUTE_SCSI; + + Srb->DataBuffer = MmGetMdlVirtualAddress(Mdl); + + // + // Save byte count of transfer in SRB Extension. + // + + Srb->DataTransferLength = Mdl->ByteCount; + + // + // Indicate auto request sense by specifying buffer and size. + // + + Srb->SenseInfoBuffer = SenseInfoBuffer; + + Srb->SenseInfoBufferLength = SENSE_BUFFER_SIZE; + + // + // Set timeout value in seconds. + // + + Srb->TimeOutValue = SCSI_DISK_TIMEOUT; + + // + // Zero statuses. + // + + Srb->SrbStatus = Srb->ScsiStatus = 0; + + // + // Indicate that 10-byte CDB's will be used. + // + + Srb->CdbLength = 10; + + // + // Fill in CDB fields. + // + + Cdb = (PCDB)Srb->Cdb; + + Cdb->CDB10.LogicalUnitNumber = PartitionContext->DiskId; + + TransferBlocks = (USHORT)(Mdl->ByteCount >> PartitionContext->SectorShift); + + // + // Move little endian values into CDB in big endian format. + // + + Cdb->CDB10.LogicalBlockByte0 = ((PFOUR_BYTE)&LogicalBlockAddress)->Byte3; + Cdb->CDB10.LogicalBlockByte1 = ((PFOUR_BYTE)&LogicalBlockAddress)->Byte2; + Cdb->CDB10.LogicalBlockByte2 = ((PFOUR_BYTE)&LogicalBlockAddress)->Byte1; + Cdb->CDB10.LogicalBlockByte3 = ((PFOUR_BYTE)&LogicalBlockAddress)->Byte0; + + Cdb->CDB10.Reserved2 = 0; + + Cdb->CDB10.TransferBlocksMsb = ((PFOUR_BYTE)&TransferBlocks)->Byte1; + Cdb->CDB10.TransferBlocksLsb = ((PFOUR_BYTE)&TransferBlocks)->Byte0; + + Cdb->CDB10.Control = 0; + + // + // Set transfer direction flag and Cdb command. + // + + Srb->SrbFlags = SRB_FLAGS_DATA_IN; + + Cdb->CDB10.OperationCode = SCSIOP_READ; + + // + // Disable synchronous transfers. + // + + Srb->SrbFlags |= SRB_FLAGS_DISABLE_SYNCH_TRANSFER; + + // + // Set up major SCSI function. + // + + NextIrpStack->MajorFunction = IRP_MJ_SCSI; + + // + // Save SRB address in next stack for port driver. + // + + NextIrpStack->Parameters.Others.Argument1 = (PVOID)Srb; + + return(Irp); + +} // end BuildReadRequest() + +VOID +ScsiDiskStartUnit( + IN PPARTITION_CONTEXT PartitionContext + ) + +/*++ + +Routine Description: + + Send command to SCSI unit to start or power up. + Because this command is issued asynchronounsly, that is without + waiting on it to complete, the IMMEDIATE flag is not set. This + means that the CDB will not return until the drive has powered up. + This should keep subsequent requests from being submitted to the + device before it has completely spun up. + This routine is called from the InterpretSense routine, when a + request sense returns data indicating that a drive must be + powered up. + +Arguments: + + PartitionContext - structure containing pointer to port device driver. + +Return Value: + + None. + +--*/ +{ + PIO_STACK_LOCATION irpStack; + PIRP irp; + SCSI_REQUEST_BLOCK srb; + PSCSI_REQUEST_BLOCK originalSrb = &PrimarySrb.Srb; + PCDB cdb; + + ScsiDebugPrint(3,"StartUnit: Enter routine\n"); + + RtlZeroMemory(&srb, sizeof(SCSI_REQUEST_BLOCK)); + + // + // Write length to SRB. + // + + srb.Length = SCSI_REQUEST_BLOCK_SIZE; + + // + // Set up SCSI bus address. + // + + srb.PathId = originalSrb->PathId; + srb.TargetId = originalSrb->TargetId; + srb.Lun = originalSrb->Lun; + + srb.Function = SRB_FUNCTION_EXECUTE_SCSI; + + // + // Zero out status. + // + + srb.ScsiStatus = srb.SrbStatus = 0; + + // + // Set timeout value large enough for drive to spin up. + // NOTE: This value is arbitrary. + // + + srb.TimeOutValue = 30; + + // + // Set the transfer length. + // + + srb.DataTransferLength = 0; + srb.SrbFlags = SRB_FLAGS_NO_DATA_TRANSFER | SRB_FLAGS_DISABLE_AUTOSENSE; + srb.SenseInfoBufferLength = 0; + srb.SenseInfoBuffer = NULL; + + // + // Build the start unit CDB. + // + + srb.CdbLength = 6; + cdb = (PCDB)srb.Cdb; + + cdb->CDB10.OperationCode = SCSIOP_START_STOP_UNIT; + cdb->START_STOP.Start = 1; + + // + // Build the IRP + // to be sent to the port driver. + // + + irp = InitializeIrp( + &PrimarySrb, + IRP_MJ_SCSI, + PartitionContext->PortDeviceObject, + NULL, + 0 + ); + + irpStack = IoGetNextIrpStackLocation(irp); + + irpStack->MajorFunction = IRP_MJ_SCSI; + + srb.OriginalRequest = irp; + + // + // Save SRB address in next stack for port driver. + // + + irpStack->Parameters.Others.Argument1 = &srb; + + // + // No need to check the following 2 returned statuses as + // SRB will have ending status. + // + + (VOID)IoCallDriver(PartitionContext->PortDeviceObject, irp); + +} // end StartUnit() + +ULONG +ClassModeSense( + IN PPARTITION_CONTEXT Context, + IN PCHAR ModeSenseBuffer, + IN ULONG Length, + IN UCHAR PageMode + ) + +/*++ + +Routine Description: + + This routine sends a mode sense command to a target id and returns + when it is complete. + +Arguments: + +Return Value: + + Length of the transferred data is returned. + +--*/ +{ + PCDB cdb; + PSCSI_REQUEST_BLOCK Srb = &PrimarySrb.Srb; + ULONG retries = 1; + NTSTATUS status; + + ScsiDebugPrint((3,"SCSI ModeSense: Enter routine\n")); + + // + // Build the read capacity CDB. + // + + Srb->CdbLength = 6; + cdb = (PCDB)Srb->Cdb; + + // + // Set timeout value. + // + + Srb->TimeOutValue = 2; + + RtlZeroMemory(cdb, MAXIMUM_CDB_SIZE); + + cdb->MODE_SENSE.OperationCode = SCSIOP_MODE_SENSE; + cdb->MODE_SENSE.PageCode = PageMode; + cdb->MODE_SENSE.AllocationLength = Length; + +Retry: + + status = SendSrbSynchronous(Context, + Srb, + ModeSenseBuffer, + Length, + FALSE); + + + if (status == EAGAIN || status == EBUSY) { + + // + // Routine SendSrbSynchronous does not retry + // requests returned with this status. + // Read Capacities should be retried + // anyway. + // + + if (retries--) { + + // + // Retry request. + // + + goto Retry; + } + } else if (SRB_STATUS(Srb->SrbStatus) == SRB_STATUS_DATA_OVERRUN) { + status = STATUS_SUCCESS; + } + + if (NT_SUCCESS(status)) { + return(Srb->DataTransferLength); + } else { + return(0); + } + +} // end ClassModeSense() + +PVOID +ClassFindModePage( + IN PCHAR ModeSenseBuffer, + IN ULONG Length, + IN UCHAR PageMode + ) + +/*++ + +Routine Description: + + This routine scans through the mode sense data and finds the requested + mode sense page code. + +Arguments: + ModeSenseBuffer - Supplies a pointer to the mode sense data. + + Length - Indicates the length of valid data. + + PageMode - Supplies the page mode to be searched for. + +Return Value: + + A pointer to the the requested mode page. If the mode page was not found + then NULL is return. + +--*/ +{ + PUCHAR limit; + + limit = ModeSenseBuffer + Length; + + // + // Skip the mode select header and block descriptors. + // + + if (Length < sizeof(MODE_PARAMETER_HEADER)) { + return(NULL); + } + + ModeSenseBuffer += sizeof(MODE_PARAMETER_HEADER) + + ((PMODE_PARAMETER_HEADER) ModeSenseBuffer)->BlockDescriptorLength; + + // + // ModeSenseBuffer now points at pages walk the pages looking for the + // requested page until the limit is reached. + // + + while (ModeSenseBuffer < limit) { + + if (((PMODE_DISCONNECT_PAGE) ModeSenseBuffer)->PageCode == PageMode) { + return(ModeSenseBuffer); + } + + // + // Adavance to the next page. + // + + ModeSenseBuffer += ((PMODE_DISCONNECT_PAGE) ModeSenseBuffer)->PageLength + 2; + } + + return(NULL); + +} + +BOOLEAN +IsFloppyDevice( + PPARTITION_CONTEXT Context + ) +/*++ + +Routine Description: + + The routine performs the necessary functioons to determinee if a device is + really a floppy rather than a harddisk. This is done by a mode sense + command. First, a check is made to see if the medimum type is set. Second + a check is made for the flexible parameters mode page. + +Arguments: + + Context - Supplies the device object to be tested. + +Return Value: + + Return TRUE if the indicated device is a floppy. + +--*/ +{ + + PVOID modeData; + PUCHAR pageData; + ULONG length; + + modeData = ExAllocatePool(NonPagedPoolCacheAligned, MODE_DATA_SIZE); + + if (modeData == NULL) { + return(FALSE); + } + + RtlZeroMemory(modeData, MODE_DATA_SIZE); + + length = ClassModeSense(Context, + modeData, + MODE_DATA_SIZE, + MODE_SENSE_RETURN_ALL); + + if (length < sizeof(MODE_PARAMETER_HEADER)) { + + // + // Retry the request in case of a check condition. + // + + length = ClassModeSense(Context, + modeData, + MODE_DATA_SIZE, + MODE_SENSE_RETURN_ALL); + + if (length < sizeof(MODE_PARAMETER_HEADER)) { + + ExFreePool(modeData); + return(FALSE); + + } + } + + if (((PMODE_PARAMETER_HEADER) modeData)->MediumType >= MODE_FD_SINGLE_SIDE + && ((PMODE_PARAMETER_HEADER) modeData)->MediumType <= MODE_FD_MAXIMUM_TYPE) { + + ScsiDebugPrint((1, "Scsidisk: MediumType value %2x, This is a floppy.\n", ((PMODE_PARAMETER_HEADER) modeData)->MediumType)); + ExFreePool(modeData); + return(TRUE); + } + + // + // Look for the flexible disk mode page. + // + + pageData = ClassFindModePage( modeData, length, MODE_PAGE_FLEXIBILE); + + if (pageData != NULL) { + + ScsiDebugPrint((1, "Scsidisk: Flexible disk page found, This is a floppy.\n")); + ExFreePool(modeData); + return(TRUE); + + } + + ExFreePool(modeData); + return(FALSE); + +} // end IsFloppyDevice() + diff --git a/private/ntos/fw/duobase/mips/stubs.c b/private/ntos/fw/duobase/mips/stubs.c new file mode 100644 index 000000000..171fd6cf1 --- /dev/null +++ b/private/ntos/fw/duobase/mips/stubs.c @@ -0,0 +1,85 @@ +/*++ + +Copyright (c) 1990 Microsoft Corporation + +Module Name: + + stubs.c + +Abstract: + + This module implements stub routines for the boot code. + +Author: + + David N. Cutler (davec) 7-Nov-1990 + +Environment: + + Kernel mode only. + +Revision History: + +--*/ + +#include "ntos.h" + +// +// Define global data. +// + +VOID +RtlInitString( + OUT PSTRING DestinationString, + IN PCSZ SourceString OPTIONAL + ) + +/*++ + +Routine Description: + + The RtlInitString function initializes an NT counted string. + The DestinationString is initialized to point to the SourceString + and the Length and MaximumLength fields of DestinationString are + initialized to the length of the SourceString, which is zero if + SourceString is not specified. + +Arguments: + + DestinationString - Pointer to the counted string to initialize + + SourceString - Optional pointer to a null terminated string that + the counted string is to point to. + + +Return Value: + + None. + +--*/ + +{ + DestinationString->Length = 0; + DestinationString->Buffer = (PCHAR)SourceString; + if (ARGUMENT_PRESENT( SourceString )) { + while (*SourceString++) { + DestinationString->Length++; + } + + DestinationString->MaximumLength = (SHORT)(DestinationString->Length+1); + } + else { + DestinationString->MaximumLength = 0; + } +} + +VOID +KeFlushIoBuffers ( + IN PMDL Mdl, + IN BOOLEAN ReadOperation, + IN BOOLEAN DmaOperation + ) + +{ + HalFlushIoBuffers (Mdl,ReadOperation,DmaOperation); +} diff --git a/private/ntos/fw/duobase/mips/video.c b/private/ntos/fw/duobase/mips/video.c new file mode 100644 index 000000000..6ee01101c --- /dev/null +++ b/private/ntos/fw/duobase/mips/video.c @@ -0,0 +1,256 @@ +#if defined(JAZZ) + +/*++ + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + jxvideo.c + +Abstract: + + This module implements the interface with the video prom initialization + code. + +Author: + + David M. Robinson (davidro) 24-Jul-1991 + +Environment: + + Kernel mode. + + +Revision History: + +--*/ + +#include "fwp.h" +#include "jxvideo.h" +#include "duoreset.h" +#include "duobase.h" + +UCHAR PromStride, PromWidth, PromSize, PromId; +UCHAR AdrShift; + +VOID +ReadVideoPromData( + IN ULONG SrcOfst, + IN ULONG DstAdr, + IN ULONG Size + ); + + +ARC_STATUS +InitializeVideoFromProm( + IN PMONITOR_CONFIGURATION_DATA Monitor + ) +/*++ + +Routine Description: + + This routine loads the code from the video prom into the specified address. + +Arguments: + + DstAdr - Address where the video rom code is to be copied. + +Return Value: + + If the video rom is not valid, returns EINVAL otherwise + return ESUCCESS + +--*/ + +{ + + VIDEO_VIRTUAL_SPACE VideoAdr; + ARC_STATUS Status; + ULONG GlobalConfig; + ULONG VideoSize; + ULONG ConfigSize; + VIDEO_PROM_CONFIGURATION PromConfig; + UCHAR IdentifierString[32]; + + // + // Temp + // + // VideoMapTlb(); + + VideoAdr.MemoryVirtualBase = VIDEO_MEMORY_VIRTUAL_BASE; + VideoAdr.ControlVirtualBase = VIDEO_CONTROL_VIRTUAL_BASE; + + PromId = READ_REGISTER_UCHAR(VIDEO_CONTROL_VIRTUAL_BASE); + + PromStride = READ_REGISTER_UCHAR(VIDEO_CONTROL_VIRTUAL_BASE+0x08); + + switch (PromStride) { + case 1: AdrShift = 0; + break; + + case 2: AdrShift = 1; + break; + + case 4: AdrShift = 2; + break; + + case 8: AdrShift = 3; + break; + default: + return EINVAL; + } + + PromWidth = READ_REGISTER_UCHAR(VIDEO_CONTROL_VIRTUAL_BASE+0x10); + if ((PromWidth != 1) && (PromWidth != 2) && (PromWidth != 4) && + (PromWidth != 8)) { + return EINVAL; + } + PromSize = READ_REGISTER_UCHAR(VIDEO_CONTROL_VIRTUAL_BASE+0x18); + if ((READ_REGISTER_UCHAR(VIDEO_CONTROL_VIRTUAL_BASE+0x20) != 'J') || + (READ_REGISTER_UCHAR(VIDEO_CONTROL_VIRTUAL_BASE+0x28) != 'a') || + (READ_REGISTER_UCHAR(VIDEO_CONTROL_VIRTUAL_BASE+0x30) != 'z') || + (READ_REGISTER_UCHAR(VIDEO_CONTROL_VIRTUAL_BASE+0x38) != 'z')) { + return EINVAL; + } + + // + // Read VIDEO_PROM_CONFIGURATION structure + // + ReadVideoPromData(8,(ULONG)&PromConfig,sizeof(PromConfig)); + + // + // Set the video size in the global config + // + + VideoSize = (PromConfig.VideoMemorySize > PromConfig.VideoControlSize ? PromConfig.VideoMemorySize : PromConfig.VideoControlSize); + + // + // Initialize size of Video space + // + // 0 -> 512K + // 1 -> 2MB + // 2 -> 8MB + // 3 -> 32MB + // + + ConfigSize = 0; + + if (VideoSize > 0x80000) { + ConfigSize = 1; + } + if (VideoSize > 0x200000) { + ConfigSize = 2; + } + + if (VideoSize > 0x800000) { + ConfigSize = 3; + } + + GlobalConfig = READ_REGISTER_ULONG(&DMA_CONTROL->Configuration.Long); + GlobalConfig = (GlobalConfig & 0x3C) | ConfigSize; + WRITE_REGISTER_ULONG(&DMA_CONTROL->Configuration.Long,GlobalConfig); + + // + // Read the identifier string + // + + ReadVideoPromData(8+sizeof(PromConfig),(ULONG)IdentifierString,32); + + // + // Copy the code from the video prom to system memory. + // The prom is copied uncached, no need to flush Dcache. + // This memory has just been tested. There has no been any + // code before -> no need to flush Icache. + // + + ReadVideoPromData(PromConfig.CodeOffset,VIDEO_PROM_CODE_UNCACHED_BASE,PromConfig.CodeSize); + + Status = InitializeVideo(&VideoAdr,Monitor); + + return Status; + +} + +VOID +ReadVideoPromData( + IN ULONG SrcOfst, + IN ULONG DstAdr, + IN ULONG Size + ) + +/*++ + +Routine Description: + + This routine copies Size bytes of data from the Video PROM starting + at SrcOfst into DstAdr. + This routine takes into account PromStride and PromWidth; + +Arguments: + + SrcOfst - Offset from the beginning of the video prom in bytes + without taking into account PromStride. + DstAdr - Address where the video rom data is to be copied. + Size - Size in bytes to copy + +Return Value: + + If the video rom is not valid, returns EINVAL otherwise + return ESUCCESS + +--*/ + +{ + +ULONG SrcAdr; +ULONG LastAdr; + + SrcAdr = SrcOfst; + LastAdr = DstAdr+Size; + + switch (PromWidth) { + + // + // Read 1 byte at a time. + // + case 1: + while ( DstAdr < LastAdr) { + *(PUCHAR)DstAdr = READ_REGISTER_UCHAR(VIDEO_CONTROL_VIRTUAL_BASE + (SrcAdr << AdrShift)); + SrcAdr+=1; + DstAdr+=1; + } + break; + + + // + // Read 2 bytes at a time. + // + + case 2: + while ( DstAdr < LastAdr) { + *(PUSHORT)DstAdr = READ_REGISTER_USHORT(VIDEO_CONTROL_VIRTUAL_BASE + (SrcAdr << AdrShift)); + SrcAdr+=1; + DstAdr+=2; + } + break; + + + // + // Read 4 bytes at a time. + // + + case 4: + case 8: + + while ( DstAdr < LastAdr) { + *(PULONG)DstAdr = READ_REGISTER_ULONG(VIDEO_CONTROL_VIRTUAL_BASE + (SrcAdr << AdrShift)); + SrcAdr+=1; + DstAdr+=4; + } + break; + + } + +} +#endif diff --git a/private/ntos/fw/duobase/mips/x4cache.s b/private/ntos/fw/duobase/mips/x4cache.s new file mode 100644 index 000000000..b77b4a26b --- /dev/null +++ b/private/ntos/fw/duobase/mips/x4cache.s @@ -0,0 +1,340 @@ +// TITLE("Cache Flush rouitnes") +//++ +// +// Copyright (c) 1993 Microsoft Corporation +// +// Module Name: +// +// x4cache.s +// +// Abstract: +// +// This module implements cache flushing routines. +// +// Author: +// +// Lluis Abello (lluis) 03-May-1993 +// +// Environment: +// +// Kernel Mode +// +// Revision History: +// +//-- + +#include <ksmips.h> +#include "duoreset.h" + +// +// Declare cache size variables. +// + +.globl FirstLevelDcacheSize +.globl FirstLevelDcacheFillSize +.globl SecondLevelDcacheSize +.globl SecondLevelDcacheFillSize +.globl DcacheFillSize +.globl DcacheAlignment +.globl FirstLevelIcacheSize +.globl FirstLevelIcacheFillSize +.globl SecondLevelIcacheSize +.globl SecondLevelIcacheFillSize + + +.data + +.align 4 +FirstLevelDcacheSize: +.space 4 +FirstLevelDcacheFillSize: +.space 4 +SecondLevelDcacheSize: +.space 4 +SecondLevelDcacheFillSize: +.space 4 +DcacheFillSize: +.space 4 +DcacheAlignment: +.space 4 +FirstLevelIcacheSize: +.space 4 +FirstLevelIcacheFillSize: +.space 4 +SecondLevelIcacheSize: +.space 4 +SecondLevelIcacheFillSize: +.space 4 + + +.text + +//++ +// +// VOID +// InitializeCacheVariables ( +// ) +// +// Routine Description: +// +// This function initializes the cache size variables +// the information is taken from the r4000 config register +// +// Arguments: +// +// None. +// +// Return Value: +// +// None. +// +//-- + + + LEAF_ENTRY(InitializeCacheVariables) + .set noreorder + .set noat + mfc0 t5,config // get configuration data + nop // 1 cycle hazard + +// +// Compute the size of the primary data cache, the primary data cache line +// size, the primary instruction cache, and the primary instruction cache +// line size. +// + + srl t0,t5,CONFIG_IC // compute instruction cache size + and t0,t0,0x7 // + addu t0,t0,12 // + li t6,1 // + sll t0,t6,t0 // t0 = I cache size + srl t2,t5,CONFIG_IB // compute instruction cache line size + and t2,t2,1 // + li t1,16 // + sll t1,t1,t2 // t1 = I cache line size + .set reorder + .set at + sw t0,FirstLevelIcacheSize // Store Values + sw t1,FirstLevelIcacheFillSize // Store Values + .set noreorder + .set noat + + + srl t0,t5,CONFIG_DC // compute data cache size + and t0,t0,0x7 // + addu t0,t0,12 // + li t2,1 // + sll t2,t2,t0 // t2 = data cache size + srl t0,t5,CONFIG_DB // compute data cache line size + and t0,t0,1 // + li t4,16 // + sll t4,t4,t0 // t2 = data cache line size + + .set reorder + .set at + sw t2,FirstLevelDcacheSize // Store Values + sw t4,FirstLevelDcacheFillSize // Store Values + .set noreorder + .set noat + +// +// Compute the size of the secondary data cache, the secondary data cache line +// size, the secondary instruction cache, and the secondary instruction cache +// line size. +// + + li t2,0 // data cache size if no secondary + li t3,0 // data cache line size if no secondary + li t6,(1 << CONFIG_SC) + and t7,t5,t6 + bne t7,zero,10f // if non-zero no secondary cache + srl t7,t5,CONFIG_SB // compute data cache line size + li t2,SECONDARY_CACHE_SIZE // compute data cache size + and t7,t7,3 // + li t3,16 // + sll t3,t3,t7 // +10: + +// +// Set the secondary instruction size, the secondary instruction cache line size, +// the secondary data cache size, and the secondary data cache line size. +// + .set reorder + .set at + + sw t2,SecondLevelIcacheSize // set size of instruction cache + sw t3,SecondLevelIcacheFillSize // set line size of instruction cache + sw t2,SecondLevelDcacheSize // set size of data cache + sw t3,SecondLevelDcacheFillSize // set line size of data cache + + .set noreorder + .set noat +// +// Set the data cache fill size and alignment values. +// + + bne zero,t2,5f // if ne, second level cache present + move t2,t3 // get second level fill size + move t2,t4 // get first level fill size + +5: .set at + .set reorder + subu t3,t2,1 // compute dcache alignment value + sw t2,DcacheFillSize // set dcache fill size + sw t3,DcacheAlignment // set dcache alignment value + j ra + + .end InitializeCacheVariables + +//++ +// +// VOID +// _RtlCheckStack ( +// IN ULONG Allocation +// ) +// +// Routine Description: +// +// This function provides a stub routine for runtime stack checking. +// +// Arguments: +// +// Allocation (t8) - Supplies the size of the allocation in bytes. +// +// Return Value: +// +// None. +// +//-- + + LEAF_ENTRY(_RtlCheckStack) + + j ra // return + + .end _RtlCheckStack + + +//++ +// +// VOID +// FwSweepIcache ( +// VOID +// ) +// +// Routine Description: +// +// This function sweeps (index/invalidate) the entire instruction cache. +// +// Arguments: +// +// None. +// +// Return Value: +// +// None. +// +//-- + + LEAF_ENTRY(FwSweepIcache) + lw t2,FirstLevelIcacheSize // load Primary I cache size + lw t3,FirstLevelIcacheFillSize// load Primary I cache Fill size + lw t0,SecondLevelIcacheSize// load Secondary I cache size + beq t0,zero,20f // if zero no secondary + lw t1,SecondLevelIcacheFillSize + + .set noreorder + .set noat + + li a0,KSEG0_BASE // set starting index value + addu a1,a0,t0 // compute ending cache address + subu a1,a1,t1 // compute ending block address + +// +// Sweep the secondary instruction cache. +// + +10: cache INDEX_INVALIDATE_SI,0(a0) // invalidate cache line + bne a0,a1,10b // if ne, more to invalidate + addu a0,a0,t1 // compute address of next block + +20: + + li a0,KSEG0_BASE // set starting index value + addu a1,a0,t2 // compute ending cache address + subu a1,a1,t3 // compute ending block address + +// +// Sweep the primary instruction cache. +// + +30: cache INDEX_INVALIDATE_I,0(a0) // invalidate cache line + bne a0,a1,30b // if ne, more to invalidate + addu a0,a0,t3 // compute address of next block + + j ra // return + nop + .end FwSweepIcache + +//++ +// +// VOID +// FwSweepDcache ( +// VOID +// ) +// +// Routine Description: +// +// This function sweeps (index/writeback/invalidate) the entire data cache. +// +// Arguments: +// +// None. +// +// Return Value: +// +// None. +// +//-- + + LEAF_ENTRY(FwSweepDcache) + .set reorder + .set at + lw t0,FirstLevelDcacheSize + lw t1,FirstLevelDcacheFillSize + li a0,KSEG0_BASE // set starting index value + addu a1,a0,t0 // compute ending cache address + subu a1,a1,t1 // compute ending block address + + .set noreorder + .set noat +// +// Sweep the primary data cache. +// + +10: cache INDEX_WRITEBACK_INVALIDATE_D,0(a0) // writeback/invalidate on index + bne a0,a1,10b // if ne, more to invalidate + addu a0,a0,t1 // compute address of next block + + .set reorder + .set at + lw t0,SecondLevelDcacheSize + beq t0,zero,30f // if zero no secondary cache + lw t1,SecondLevelDcacheFillSize + .set noreorder + .set noat + + li a0,KSEG0_BASE // set starting index value + addu a1,a0,t0 // compute ending cache address + subu a1,a1,t1 // compute ending block address + +// +// Sweep the secondary data cache. +// + +20: cache INDEX_WRITEBACK_INVALIDATE_SD,0(a0) // writeback/invalidate on index + bne a0,a1,20b // if ne, more to invalidate + addu a0,a0,t1 // compute address of next block + +30: j ra // return + nop + .end FwSweepDcache |