summaryrefslogtreecommitdiffstats
path: root/private/ntos/fw/duobase/mips
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--private/ntos/fw/duobase/mips/bldr.h258
-rw-r--r--private/ntos/fw/duobase/mips/duobase.c199
-rw-r--r--private/ntos/fw/duobase/mips/duobase.h66
-rw-r--r--private/ntos/fw/duobase/mips/duoreset.h121
-rw-r--r--private/ntos/fw/duobase/mips/duoreset.s1942
-rw-r--r--private/ntos/fw/duobase/mips/fatboot.c2307
-rw-r--r--private/ntos/fw/duobase/mips/fatboot.h270
-rw-r--r--private/ntos/fw/duobase/mips/firmware.h135
-rw-r--r--private/ntos/fw/duobase/mips/fwio.c1277
-rw-r--r--private/ntos/fw/duobase/mips/fwload.c301
-rw-r--r--private/ntos/fw/duobase/mips/fwp.h633
-rw-r--r--private/ntos/fw/duobase/mips/jxdisp.c2086
-rw-r--r--private/ntos/fw/duobase/mips/jxfwhal.h95
-rw-r--r--private/ntos/fw/duobase/mips/jxhwsup.c927
-rw-r--r--private/ntos/fw/duobase/mips/romsetup.c1097
-rw-r--r--private/ntos/fw/duobase/mips/scsiboot.c4664
-rw-r--r--private/ntos/fw/duobase/mips/scsiboot.h462
-rw-r--r--private/ntos/fw/duobase/mips/scsidisk.c2037
-rw-r--r--private/ntos/fw/duobase/mips/stubs.c85
-rw-r--r--private/ntos/fw/duobase/mips/video.c256
-rw-r--r--private/ntos/fw/duobase/mips/x4cache.s340
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, &currentIrql);
+
+ //
+ // 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