The figure shows a high-level architecture of the OS 2200 system identifying major hardware and software components. The majority of the Unisys software is included in the subsystems and applications area of the model. For example, the database managers are subsystems and the compilers are applications.
The details of the system architecture are covered in Unisys publication 3850 7802 Instruction Processor Programming Reference Manual. Also see UNIVAC 1100/2200 series.
The 1100 Series has used a 36-bit word with 6-bit characters since 1962. 36-bit computing was driven by a desire to process 10-digit positive and negative numbers. Also the military needed to be able to calculate accurate trajectories, design bridges, and perform other engineering and scientific calculations, they needed more than 32 bits of precision. A 32-bit floating point number only provided about 6 digits of accuracy while a 36 bit number provided the 8 digits of accuracy that were accepted as the minimum requirement. Since memory and storage space and costs drove the system, going to 64 bits was simply not acceptable in general. These systems use ones' complement arithmetic, which was not unusual at the time. Almost all computer manufacturers of the time delivered 36-bit systems with 6-bit characters including IBM, DEC, General Electric, and Sylvania.
The 6-bit character set used by the 1100 Series is also a DoD mandated set. It was defined by the Army Signal Corps and called Fieldata (data returned from the field). [1] The 1108 provided a 9-bit character format in order to support ASCII and later the ISO 8-bit sets, but they were not extensively used until the 1980s again because of space constraints.
The 2200 Series architecture provides many registers. Base registers logically contain a virtual address that points to a word in a code or data bank (segment). They may point to the beginning of the bank or to any word within the bank. Index registers are used by instructions to modify the offset of the specified or assumed base register. Simple arithmetic (add, subtract) may be performed on all index registers. In addition, index registers consist of a lower offset portion and an upper increment portion. An instruction may both use the offset value in an index register as part of an address and specify that the increment is to be added to the offset. This allows loops to be accomplished with fewer instructions as incrementing the index by the step size can be accomplished without a separate instruction. Arithmetic registers allow the full set of computational instructions including all floating point operations. Some of those instructions work on adjacent pairs of registers to perform double-precision operations. There are no even-odd constraints. Any two registers may be used as a double-precision value. Four of the arithmetic registers are also index registers (the sets overlap – index register X12 is arithmetic register A0). This allows the full range of calculations to be performed on indexes without having to move the results. The rest of the registers, known as R registers, are used as fast temporary storage and for certain special functions. R1 holds the repeat count for those instructions that may be repeated (block transfer, execute repeated, etc.). R2 holds a bit mask for a few instructions that perform a bitwise logical operation in addition to some other functions (e.g., masked load upper)
There are two full sets of registers (A, X, R, and B). One set, the user registers, is used by all applications and most portions of the operating system. It is saved and restored as part of activity (thread) state. The other set, the Exec registers, is used by interrupt processing routines and some other portions of the operating system that want to avoid having to save and restore user registers. The Exec registers are not writable by user applications although some user code can read them. As a result, the Exec is carefully designed never to leave private, secure, or confidential information in registers. Instruction interpretation chooses the appropriate register set to use based on a bit in the Processor State Register. This bit is always set (changed to privileged) on an interrupt. All registers are also visible in the address space, but the Exec portion is protected and a reference by non-privileged code will result in a fault interrupt.
The 2200 Series uses a 36-bit segmented virtual address space. We’ll look later at the addressing architecture.
The 2200 Series is a CISC architecture system. Not only are there a large number of instructions (current count is about 245) but many of them have addressing variants. Some of the variants are encoded directly in the instruction format (partial word references) and some are dependent on Processor State Register settings. Many instructions also perform very complex functions such as one that implements a large part of the COBOL EDIT verb.
The above figure shows some of the building blocks of the architecture. "Data" and "COMM" are two of the primary examples of software subsystems that live in a protection ring between that of a user application and the Exec. There are many other such subsystems and users write their own.
As was mentioned earlier the 2200 Series uses a 36-bit segmented virtual address. The original notion of a segmented space came from the earliest implementation that emphasized code and data separation for performance and the use of shared code banks. Over the years this expanded to provide greater flexibility of levels of sharing and far greater protection for security and reliability. Controlled access to shared data was also introduced.
A virtual address consists of three parts. The high-order 3 bits define the sharing level. This is the heart of the entire addressing and protection scheme. Every thread has eight Bank Descriptor Tables (Segment Descriptor Tables in the industry) based on B16-B23. The tables are indexed by level – level 0 refers to the Bank Descriptor Table (BDT) based on B16, level 2 the BDT based on B18, etc. The level 0 and level 2 BDTs are common to all threads in the system. Every run (process) has its own level 4 BDT, and that BDT is common to all threads in the run. Every user thread has its own unshared level 6 BDT.
Each extended-mode activity (thread) always has six banks, segments, which are totally unique to it. One is the Return Control Stack which holds information about the calling structure including any security relevant privilege and state changes. It is not accessible by the thread except through the use of the CALL, RETURN, and similar instructions. This is a major part of the protection and reliability mechanism. Applications cannot cause bad effects by changing the return addresses or overwriting the return control stack. [2]
Another unique bank is the automatic storage bank (Activity Local Store stack). This is used by the compilers to hold local variables created within a block. It is also used to hold all parameter lists passed on a call. One of the checks made by the operating system both on its own behalf and when a call is made to a protected subsystem is to ensure that the operands are on the thread-local stack and that the thread has the right to access the memory region referenced by any parameters. Because the parameters are kept in thread-local space, there is no chance that some other thread may change them during or after validation. It is the responsibility of the called procedure to perform similar checks on any secondary parameters that may exist in shared space (i.e., the primary parameter points to a structure that contains pointers). The procedure is expected to copy any such pointers to its own local space before validating them and then to use only that internally held validated pointer.
Activities may create additional segments up to the limit of the available address space (233 words = 8GW or about 36GB). This is a convenient way for multi-threaded applications to get large amounts of memory space knowing that it is totally thread-safe and that they are not taking any space away from the rest of what is available to the program. Each activity in a program has its own independent space meaning an application with say 100 activities is able to use over 800GW (>3TB) of virtual space.
Basic-mode activities do not start out with any such banks as basic-mode programs are not aware of the virtual address space, but any calls to extended-mode subsystems will cause those banks to be created.
OS 2200 does not implement programs in exactly the same way that UNIX, Linux, and Windows implement processes, but that is the closest analogy. The most obvious difference is that OS 2200 only permits a single program per Run (Job, Session) to be executing at a time. A program may have hundreds of threads, but cannot spawn other programs to run concurrently.[ citation needed ]
There are several banks at the Program level that contain a mixture of Run (job, session) information and program information. These are control structures for the operating system. They have no access or read-only access for the program. Programs may retrieve information from some of these structures for debugging purposes or to retrieve things like the user-id and terminal-id without the overhead of a system call. They cannot be written by the program. They contain things like the thread state save areas, file control blocks, and accounting information.
The rest of the banks are used by the program. When a program object file is executed, the operating system obtains the bank information from the file and creates banks as needed and loads the bank initial state from the file. The simplest program has a single bank containing code and data. This is considered very bad form, but is permitted for compatibility with old applications. You can only create such an application with assembly language. The standard compilers create one or more code banks and one or more data banks. Normally the code banks are marked as read-only as a debugging and reliability aid. There are no security concerns either way. The program can only affect itself.
Each program thus has its own address space distinct from all other programs in the system. Nothing a program can do can change the contents of any other program’s memory. The OS and shared subsystems are protected by other mechanisms which will be discussed later. Even read access is prohibited to OS and subsystem memory in almost all cases from code in a program. It is possible to create a shared subsystem which is generally readable, or even writable, by multiple programs, but it must be explicitly installed that way by a privileged system administrator. Programs are initially created with just the banks specified in the object file and with a single activity. They may use system calls to create additional banks within their own program level and additional activities.
The closest analogy to a shared subsystem is a .dll. A subsystem is much like a program in many respects except that it does not have any activities associated with it. Instead it is accessed by other programs and subsystems typically via a CALL instruction. In fact, a program is a subsystem plus one or more activities. Every activity belongs to a "home" subsystem which is the program that created it. This subsystem concept is important as an encapsulation of access rights and privilege. Within their home subsystem, activities typically share common access rights to code and data banks. Code banks in the home subsystem are usually read-only, or even execute-only if they contain no constant data, but all activities will have the right to execute them. [3]
Subsystems are also combinations of banks and may contain data banks as well as code banks. All globally shared subsystems must be installed in the system by someone with appropriate administrator privileges. Subsystems may also open files. The Database manager is a subsystem which opens all the database files for its use typically with exclusive access rights. The operating system will attach its own banks to a subsystem to hold the file control tables.
The OS level contains the banks of the Exec. These banks are never directly accessible by either programs or global subsystems. Entry points to the OS are all handled in the same way as a protected subsystem. Calls made to the OS are always via "gates," instructions that exist for that purpose (ER = Executive Request), or via interrupts.
The next part of the virtual address is the BDI or Bank Descriptor Index. The Level field selected a particular bank descriptor table base register (B16-B23). Base registers B16-B23 are part of the activity state and are maintained by the Exec with no direct access by the activity. The Bank Descriptor tables for the program and activity levels exist within the program-level banks that belong to the operating system.
The BDI is simply an index into a Bank Descriptor Table. Each entry in the table contains information about a bank. Each such entry describes up to 1MB (256KW) of virtual address space. When a larger contiguous space is needed, consecutive entries are logically combined to create a larger bank up to the maximum of 230 words.
The Bank Descriptor Table Entry (Bank Descriptor – BD) gives the size of the bank (small = up to 256KW, large = up to 16MW, very large = up to 1GW). A small bank is always represented by a single BD. Large banks are represented by up to 64 consecutive BDs and a very large bank by up to 4096 BDs. Large and very large banks need not use all 64 or 4096 consecutive BDs. They only use as many as needed to provide the virtual address space required. The entry also contains upper and lower limits of allowable offsets within the bank. Virtual addresses that are outside the limits generate a fault interrupt. This allows small banks, for example containing a message, to have only the virtual space reserved for it that it actually needs and provides a debugging check against bad pointers and indices.
The BD also contains a key value and access control fields. The fields indicate whether read, write, or execute permission is granted to the instruction processor (3 bits). The Special Access Permissions (SAP) applies only to activities executing within the owning subsystem (really only those with a matching key value). The General Access Permissions (GAP) applies to everyone else and is usually zero (no access). The Exec sets a key value in the state of each activity which may be changed by gate and interrupt transitions.
The 2200 Series protection architecture uses three pieces of activity state that are reflected in the hardware state. They are Processor Privilege (PP), Ring, and Domain.
Processor Privilege controls the ability to execute privileged instructions and access protected registers and other state. PP=0 is used by the Exec and gives full access to all instructions and privileged state. Exec activities and user activities that have used a gate to access an Exec API run at PP=0.
PP=1 restricts most privileged instructions but does allow reading of the day clocks and reading the contents of some of the privileged registers. None of the privileged registers contain any truly sensitive information, but allowing general read access could easily lead to undetected errors in user programs. Basically at PP=1, instructions that can change the addressing environment, change the clocks, change instrumentation state, or perform I/O are all restricted. PP=1 is rarely used.
PP=2 is the normal user mode and is state in which all other code executes. It is a further restriction of PP=1.
There is also a PP=3 which further restricts the instructions a user program can execute, but it is not currently in use as too many existing programs were using some of those instructions. The intent was to restrict access to instructions that may be system model dependent.
The Domain mechanism is the heart of the protection mechanism. Each BD (bank descriptor) has a lock field consisting of a ring number and domain number. There is also a key field in the state of each activity. If the key matches the lock or the ring in the key is less than the ring in the lock, the activity has Special Access Permission. Otherwise, the activity has General Access Permission.
Ring allows overriding the Domain protection mechanism. User applications run at Ring=3. Protected subsystems run at Ring=2. This gives them access to their own data while still allowing them to access parameters and data in the calling user’s space. Note that it is still not possible for a thread to cause the protected subsystem to access some other user’s space as only this thread’s Bank Descriptor Tables are in use. Ring=0 is used by the OS and allows it to access its own data while still being able to access parameters passed from either user programs or protected subsystems.
Gates are another part of the protection mechanism. A gate is a data structure that controls transitions between domains. A gate lives in a gate bank and the hardware enforces that all references to gates must be to addresses at a proper offset (multiple of a gate size) within a gate bank. A gate contains the target address, new values for PP, Ring, and Domain, and may contain a hidden parameter to be passed to the target. Protected subsystems are not directly accessible to other subsystems. Instead a subsystem must request that a gate be built in its gate bank for access to that subsystem. This permits the operating system to perform any access control checks. The linking system will then find the gate address associated with an entry point. In fact, the whole mechanism is usually transparently handled within the linking system. The hidden parameter permits, for example, a file I/O gate to contain the address or handle of the file control block. Since this is guaranteed to be correct as it was created by the OS when the user opened the file, many error checks can be eliminated from the path length to do I/O.
OS 2200 is designed to handle up to 32 instruction processors (or CPUs). A great deal of design has been done over the years optimize for this environment. For example, OS 2200 makes almost no use of critical sections in its design. There’s too high a probability of multiple processors executing the same code. Instead it uses data locking on the finest granularity data possible. Generally locks deal with a single instance of a data object (e.g., activity control structure or file control block) and are contained within the data structure of the object. This minimizes the likelihood of conflicts. When more global locks have to be set as when updating a list of objects, the lock is set only as long as it takes to update the links in the list. Even dispatching is done with separate locks for different priority levels. A check can be made for an empty priority level without setting a lock. The lock need only be set when adding or removing an item from the queue.
The register set is in the visible address space. Registers appear to exist in the first 128 words (2008) of the current instruction bank (B0) when referenced as a data item. This does impose a restriction on compilers to not place any data constants in the first 128 words of a code bank. The result of this is an expansion of the instruction set without requiring additional operation codes. Register-to-register operations are accomplished with the register-storage operation codes.
Typical instructions contain a function code, the target (or source) register, an index register, a base register and a displacement field. When the function code with its qualifier indicates immediate data, the displacement, base, i, and h fields combine to form a single 18-bit immediate value. This allows loading, adding, multiplying, etc. by small constants to eliminate a memory reference and the associated storage.
Processor state as captured on a stack at an interrupt contains the information needed to both return control to the interrupted activity and to determine the type of the interrupt. Interrupts may occur in the middle of long instructions and the state deals with that possibility.
Basic mode is another whole form of instruction formats and addressing. Basic mode provides compatibility with previous systems back to the 1108. For all practical purposes, the hardware architecture defines the rules by which addresses and instructions are converted to the above forms. The most obvious difference in basic mode is the lack of explicit B registers in instructions. Instead there are four implicitly used B registers (B12-B15). There is a complex algorithm using the limits of the banks represented by those B registers, the operand address and the B register within which the current instruction is found.
The most interesting instructions in the 2200 repertoire are the locking and synchronization instructions. Conditional replace is familiar and quite similar to Compare and Swap in the Intel architecture. These instructions always gain exclusive use of the memory/cache-line holding the referenced word. TS and TSS check a bit in the referenced word. If the bit is clear, they set it and continue (TS) or skip (TSS). If the bit is set, they either interrupt (TS) or fall through to the next instruction(TSS). On a TS interrupt the OS takes one of several actions depending on the instruction sequence and activity priority. Real time and Exec activities simply get control back to allow retry unless there is an even higher-priority activity waiting. The presumption is that the lock is set on another processor and will soon be cleared. If it is a user activity not running at real time priority, it may have its priority temporarily reduced and be placed back in the dispatching queues.
Alternatively, the code sequence may indicate that Test & Set Queuing is being used. In this case, the OS places the activity in a wait state and chains it to the end of the list of activities waiting for that particular lock. Activities clearing such a lock check to see if any are waiting and if so notify the OS to allow one of more to try again. Test & Set Queuing is typically used for synchronization within subsystems such as the database manager where activities from many programs may be executing.
The result of these mechanisms is very efficient, low overhead, synchronization among activities.
The queuing architecture is another interesting special case. It was specifically designed to allow very efficient handling of messaging where the number of messages waiting for processing could be almost unlimited. It is also aimed at reducing one of the primary costs of messaging, namely having to constantly move messages around in memory. Even moving them from the communication manager to the message queue subsystem to the processing program is eliminated. Instead each message is placed in a small bank of its own. Instructions allow placing the bank descriptors of these banks in a queue and removing them from a queue. When a message is placed in a queue, the sending program or subsystem no longer has any access to it. That bank is removed from its address space. When a message is retrieved from a queue, the bank becomes part of the receiver's address space. The queuing instructions also provide activity synchronization functions (e.g., wait for a message).
Only "pointers" are moved and they are moved in a way that ensures security and integrity. Once moved, the data in the message is only visible to the recipient.
All I/O on 2200 Series systems is handled by I/O processors. These processors offload large portions of the I/O path length and recovery, and by fully isolating the main system from I/O faults, interrupts, bus errors, etc. greatly improve reliability and availability. The I/O processors come in three different types (Storage, Communications, Clustering) but the only real difference is the firmware load. [4]
All I/O processors are controlled by the operating system. OS 2200 does provide a raw mode for I/O called "arbitrary device I/O," but even there the OS validates that the program is accessing an allowed device and handles all interrupts and faults before passing appropriate status on to the program. Programs must be granted privileges by the security officer to access devices in arbitrary mode and that may be limited by both the security officer and the system operator to specific devices. Arbitrary I/O is not allowed to a device that is also in use by any other program or the system. The device must be exclusively allocated to the program.
The OS takes very general calls from programs and generates command packets with real memory and device addresses which are then passed to the I/O processor. Firmware in the I/O processor actually creates the device specific (e.g., SCSI) packets, sets up the DMA, issues the I/O, and services the interrupts.
In computing, a context switch is the process of storing the state of a process or thread, so that it can be restored and resume execution at a later point, and then restoring a different, previously saved, state. This allows multiple processes to share a single central processing unit (CPU), and is an essential feature of a multiprogramming or multitasking operating system. In a traditional CPU, each process - a program in execution - utilizes the various CPU registers to store data and hold the current state of the running process. However, in a multitasking operating system, the operating system switches between processes or threads to allow the execution of multiple processes simultaneously. For every switch, the operating system must save the state of the currently running process, followed by loading the next process state, which will run on the CPU. This sequence of operations that stores the state of the running process and the loading of the following running process is called a context switch.
An operating system (OS) is system software that manages computer hardware and software resources, and provides common services for computer programs.
In computing, a system call is the programmatic way in which a computer program requests a service from the operating system on which it is executed. This may include hardware-related services, creation and execution of new processes, and communication with integral kernel services such as process scheduling. System calls provide an essential interface between a process and the operating system.
UNIVAC was a line of electronic digital stored-program computers starting with the products of the Eckert–Mauchly Computer Corporation. Later the name was applied to a division of the Remington Rand company and successor organizations.
x86 memory segmentation refers to the implementation of memory segmentation in the Intel x86 computer instruction set architecture. Segmentation was introduced on the Intel 8086 in 1978 as a way to allow programs to address more than 64 KB (65,536 bytes) of memory. The Intel 80286 introduced a second version of segmentation in 1982 that added support for virtual memory and memory protection. At this point the original mode was renamed to real mode, and the new version was named protected mode. The x86-64 architecture, introduced in 2003, has largely dropped support for segmentation in 64-bit mode.
In computing, protected mode, also called protected virtual address mode, is an operational mode of x86-compatible central processing units (CPUs). It allows system software to use features such as segmentation, virtual memory, paging and safe multi-tasking designed to increase an operating system's control over application software.
A memory management unit (MMU), sometimes called paged memory management unit (PMMU), is a computer hardware unit that examines all memory references on the memory bus, translating these requests, known as virtual memory addresses, into physical addresses in main memory.
The UNIVAC 1100/2200 series is a series of compatible 36-bit computer systems, beginning with the UNIVAC 1107 in 1962, initially made by Sperry Rand. The series continues to be supported today by Unisys Corporation as the ClearPath Dorado Series. The solid-state 1107 model number was in the same sequence as the earlier vacuum-tube computers, but the early computers were not compatible with the solid-state successors.
The Burroughs Large Systems Group produced a family of large 48-bit mainframes using stack machine instruction sets with dense syllables. The first machine in the family was the B5000 in 1961, which was optimized for compiling ALGOL 60 programs extremely well, using single-pass compilers. The B5000 evolved into the B5500 and the B5700. Subsequent major redesigns include the B6500/B6700 line and its successors, as well as the separate B8500 line.
In computer systems a loader is the part of an operating system that is responsible for loading programs and libraries. It is one of the essential stages in the process of starting a program, as it places programs into memory and prepares them for execution. Loading a program involves either memory-mapping or copying the contents of the executable file containing the program instructions into memory, and then carrying out other required preparatory tasks to prepare the executable for running. Once loading is complete, the operating system starts the program by passing control to the loaded program code.
Memory protection is a way to control memory access rights on a computer, and is a part of most modern instruction set architectures and operating systems. The main purpose of memory protection is to prevent a process from accessing memory that has not been allocated to it. This prevents a bug or malware within a process from affecting other processes, or the operating system itself. Protection may encompass all accesses to a specified area of memory, write accesses, or attempts to execute the contents of the area. An attempt to access unauthorized memory results in a hardware fault, e.g., a segmentation fault, storage violation exception, generally causing abnormal termination of the offending process. Memory protection for computer security includes additional techniques such as address space layout randomization and executable space protection.
A general protection fault (GPF) in the x86 instruction set architectures (ISAs) is a fault initiated by ISA-defined protection mechanisms in response to an access violation caused by some running code, either in the kernel or a user program. The mechanism is first described in Intel manuals and datasheets for the Intel 80286 CPU, which was introduced in 1983; it is also described in section 9.8.13 in the Intel 80386 programmer's reference manual from 1986. A general protection fault is implemented as an interrupt. Some operating systems may also classify some exceptions not related to access violations, such as illegal opcode exceptions, as general protection faults, even though they have nothing to do with memory protection. If a CPU detects a protection violation, it stops executing the code and sends a GPF interrupt. In most cases, the operating system removes the failing process from the execution queue, signals the user, and continues executing other processes. If, however, the operating system fails to catch the general protection fault, i.e. another protection violation occurs before the operating system returns from the previous GPF interrupt, the CPU signals a double fault, stopping the operating system. If yet another failure occurs, the CPU is unable to recover; since 80286, the CPU enters a special halt state called "Shutdown", which can only be exited through a hardware reset. The IBM PC AT, the first PC-compatible system to contain an 80286, has hardware that detects the Shutdown state and automatically resets the CPU when it occurs. All descendants of the PC AT do the same, so in a PC, a triple fault causes an immediate system reset.
The Burroughs B2500 through Burroughs B4900 was a series of mainframe computers developed and manufactured by Burroughs Corporation in Pasadena, California, United States, from 1966 to 1991. They were aimed at the business world with an instruction set optimized for the COBOL programming language. They were also known as Burroughs Medium Systems, by contrast with the Burroughs Large Systems and Burroughs Small Systems.
In computer science, asynchronous I/O is a form of input/output processing that permits other processing to continue before the transmission has finished. A name used for asynchronous I/O in the Windows API is overlapped I/O.
The Global Descriptor Table (GDT) is a data structure used by Intel x86-family processors starting with the 80286 in order to define the characteristics of the various memory areas used during program execution, including the base address, the size, and access privileges like executability and writability. These memory areas are called segments in Intel terminology.
A call gate is a mechanism in Intel's x86 architecture for changing the privilege level of a process when it executes a predefined function call using a CALL FAR instruction.
A process is a program in execution, and an integral part of any modern-day operating system (OS). The OS must allocate resources to processes, enable processes to share and exchange information, protect the resources of each process from other processes and enable synchronization among processes. To meet these requirements, the OS must maintain a data structure for each process, which describes the state and resource ownership of that process, and which enables the OS to exert control over each process.
OS 2200 is the operating system for the Unisys ClearPath Dorado family of mainframe systems. The operating system kernel of OS 2200 is a lineal descendant of Exec 8 for the UNIVAC 1108. Documentation and other information on current and past Unisys systems can be found on the Unisys public support website.
The OS 2200 database managers are all part of the Universal Data System (UDS). UDS provides a common control structure for multiple different data models. Flat files, network (DMS), and relational (RDMS) data models all share a common locking, recovery, and clustering mechanism. OS 2200 applications can use any mixture of these data models along with the high-volume transaction file system within the same program while retaining a single common recovery mechanism.
The OS 2200 communications management system includes CPComm and MCB along with many programs that provide communications related functions such as file transfer, e-mail, and distributed transaction processing protocols.