1 Introduction
2 Ground Rules

Building a File System
3 File Systems
4 File Content Data Structure
5 Allocation Cluster Manager
6 Exceptions and Emancipation
7 Base Classes, Testing, and More
8 File Meta Data
9 Native File Class
10 Our File System
11 Allocation Table
12 File System Support Code
13 Initializing the File System
14 Contiguous Files
15 Rebuilding the File System
16 Native File System Support Methods
17 Lookups, Wildcards, and Unicode, Oh My
18 Finishing the File System Class

The Init Program
19 Hardware Abstraction and UOS Architecture
20 Init Command Mode
21 Using Our File System
22 Hardware and Device Lists
23 Fun with Stores: Partitions
24 Fun with Stores: RAID
25 Fun with Stores: RAM Disks
26 Init wrap-up

The Executive
27 Overview of The Executive
28 Starting the Kernel
29 The Kernel
30 Making a Store Bootable
31 The MMC
32 The HMC
33 Loading the components
34 Using the File Processor
35 Symbols and the SSC
36 The File Processor and Device Management
37 The File Processor and File System Management
38 Finishing Executive Startup

Users and Security
39 Introduction to Users and Security
40 More Fun With Stores: File Heaps
41 File Heaps, part 2
42 SysUAF
43 TUser
44 SysUAF API

UCL
45 Shells and UCL

Glossary/Index


Download sources
Download binaries

Making a Store Bootable

In the previous article, we discussed how we need pointers to components inside the boot block and file system headers. In this article, we will discuss that process, and related issues. Making a store bootable is technically very easy - we just provide bootstrap code that is written to the first sector of our store. Exactly what that code looks like is up to the targeted machine architecture. But to boot up UOS is more complicated. Essentially, booting UOS involves three steps:

  • Hardware loads the primary bootstrap (in boot block) to low memory
  • Primary bootstrap loads the secondary bootstrap (Init) to high memory
  • Init loads UOS
The reason that we can't combine the first two steps into a single bootstrap is because most hardware only loads the single starting sector of the store (the boot block), so the primary bootstrap has to fit entirely within that block. But that code can load an arbitrarily larger image called the secondary bootstrap - known to us as Init.

The UOS File System Structure
As we discussed in the previous article, the pointers in the boot block and the file system header tell us the physical location and size of the various required components, but these components are also files, which means they must exist someone in the file structure itself. So, we must take a short detour and look at the UOS file system structure. Note that we are not talking about the data structure of the UOS Native File System, which was discussed at length many articles ago. Rather, we are talking about the directory and file layout that UOS uses - regardless of what file system is being used.

As we discussed before, the root directory of the file structure contains a Store directory which contains files related to the store itself. Even if we are booting from, say, a FAT32 file system, there will still be a Store directory in the root directory - although all it will contain is the UOS file system header. UOS adds some additional directories to the root directory to specifically support UOS. For now we will look only at the UOS directory. This directory contains all of the operating-system-specific files. Other files, such as user files, go elsewhere which we will discuss in the future. Within the UOS directory are any number of directories, each of which contains a full copy of UOS. On most systems, that means that there will only be a single directory under UOS. But UOS supports multiple copies so that, if desired, you can go back to older versions of UOS. For instance, let's say you upgrade from UOS Version 7 to UOS Version 8. A new directory would be created for version 8, separate from your existing Version 7 directory. Afterwards, it will boot from the new Version 8 UOS, but the old Version 7 is still there in case something goes horribly wrong and you need to revert back to the previous version. The names of these directories is arbitrary and can be chosen by the person doing the install. So how does Init know which copy to boot up if there are multiples? During install, or in other cases, the copy of UOS to boot is stored in another file in the top-level UOS directory. Actually, there are two files: installed.sys and default.sys. Default points to the default copy to boot which can be changed, or overridden on a given boot. In any case, installed.sys points to the directory for the actually booted copy of UOS. Both of these files are what we call links. On Linux, they are called symbolic links. Essentially, they are just pointers to some other file. In fact, internally, the link file data is the name of the file that the link points to. From the user's standpoint, opening a link is the same as opening the file that it points to. Since Init makes sure that the installed.sys link points to the directory containing the copy of UOS that we are booting, it is what UOS uses to determine which set of files to use. On a file system without links support, these files are simply text files containing the name of the target directory.

Within each UOS subdirectory, are the files for that version of UOS. This includes the MMC, HMC, Kernel, and File System component files that are pointed to by the various pointers in the boot block and file system headers. Thus, making UOS bootable from a store is really nothing more than making sure that the aforementioned files are present, contiguous, and pointed to in the boot block and file system header. This process is called hooking UOS and can be done from Init or while UOS is running.

Installing UOS
We will discuss installation of UOS in more detail in the future, but we will look at it briefly here since it is necessary to get UOS running on the simulator. First, one needs installation media. This can be anything from a DVDROM, to a USB drive, to a magnetic tape. UOS doesn't care what kind of media is used, so long as it has the necessary files on it. So far, our UOS code doesn't support DVDROMs, magnetic tapes, or USB drives. So, when we finish this set of articles, I will be providing a disk image which is a UOS installation media. For those of you who are curious as to how I created an installation media without any of those other kinds of media, the simulator has been modified to allow direct access to the file system on a virtual disk. I simply used it to create the UOS directory, a subdirectory, and then copied the necessary files into that subdirectory. You can look at it upon the next release after this set of articles.

So, with our installation disk available, when we boot up from our ROM image in the simulator, we can then tell it to boot from whichever disk is the installation disk. Once that disk is booted, we can tell Init to do an install. Installs simply involve copying from the current UOS installation to whatever target directory you want. If there is no installed.sys link (which there never is on an installation media), it looks at the first subdirectory under UOS (the only subdirectory on an installation disk), and copies the files from there. It then hooks the new copy of UOS on the target store. Note that if the installation media is writeable, you could install onto that media as well. Or, if you have a diskless system, you can simply boot from the installation media itself. UOS doesn't care - all it needs is a store with the UOS files in the appropriate UOS directory. Again, we will revisit installs in more detail in the future.

Here is the Init code for doing installs.

procedure Install( S : string ) ;

var Boot_Header : FS_Boot_Record ;
    Buff : array[ 0..511 ] of byte ;
    Buffer : PChar ;
    Context : pointer ;
    Count : int64 ;
    Fil : TUOS_File ;
    FS : TUOS_Native_File_System ;
    Index : integer ;
    IFlags, Info : TUOS_File_Info ;
    MStore : TUOS_Managed_Store ;
    Original, S1, S2, S3 : string ;
    P : int64 ;
    Store : TCOM_Store64 ;
    Store_Header : FS_Header ;
    UE : TUnified_Exception ;
    UF, TF : TUOS_File ;

label Ask_Default, Ask_Name, Loop ;

begin
    Original := S ;
Loop:
    // Get store to install to and do checks...
    if( Original = '' ) then
    begin
        Output( 'Disk to install to <DISKA0> : ' ) ;
        S := Input( 'DISKA0' ) ;
        case( Process_Input( S, S1, True ) ) of
            1 : begin
                    exit ;
                end ;
            2 : begin
                    exit ;
                end ;
        end ;
    end else
    begin
        S := Original ;
    end ;
    Store := Parse_Device( S ) ;
    if( Store = nil ) then
    begin
        Output_Line( 'Invalid device' ) ;
        if( Original = '' ) then
        begin
            goto Loop ;
        end ;
        exit ;
    end ;
    if( not Valid_File_System( Store ) ) then
    begin
        Output_Line( 'Device has no file system' ) ;
        if( Original = '' ) then
        begin
            goto Loop ;
        end ;
        exit ;
    end ;
    if( Store.Read_Only ) then
    begin
        Output_Line( 'Device is write-locked' ) ;
        if( Original = '' ) then
        begin
            goto Loop ;
        end ;
        exit ;
    end ;

First we get the store name, either passed to us or, if not, we ask the user. Then we verify that it is a valid store, has a file system on it, and that is isn't read-only.

Ask_Name:
    S3 := '' ;
    MStore := TUOS_Managed_Store.Create ;
    MStore.Store := Store ;
    FS := TUOS_Native_File_System.Create ;
    FS.Store := MStore ;
    try
        Index := 1 ;
        while( true ) do
        begin
            Info := FS.Get_File_Info( PChar( '\uos\UOS' + inttostr( Index ) ), 0 ) ;
            if( FS.Last_Error = nil ) then // Found folder
            begin
                inc( Index ) ;
                S3 := 'YES' ; // There are other installs
            end else
            begin
                break
            end ;
        end ; // while( True ) ;
    finally
        FS.Free ;
        MStore.Free ;
    end ;
    Output( 'Name for install <UOS' + inttostr( Index ) + '> : ' ) ;
    S := Input( 'UOS' + inttostr( Index ) ) ;
    case( Process_Input( S, S1, True ) ) of
        1 : begin
                exit ;
            end ;
        2 : begin
                if( Original = '' ) then
                begin
                    goto Loop ;
                end ;
                exit ;
            end ;
    end ;

Next we ask the user for an install name. This will be the name of the directory for the copy of UOS. We default the name to "UOS" suffixed by a number. The loop goes through all possible numbers starting with 1 (for instance "UOS1", "UOS2", "UOS3", etc.) until we find a directory name that does not exist.

    MStore := TUOS_Managed_Store.Create ;
    MStore.Store := Store ;
    FS := TUOS_Native_File_System.Create ;
    FS.Store := MStore ;
    UE := FS.Mount( '', '', 0 ) ;
    if( ( UE <> nil ) and ( UE.Get_Error = UOS_File_System_Is_Dirty ) ) then
    begin
        Output( 'Disk was not dismounted properly - rebuilding...' ) ;
        FS.Rebuild ;
        UE := FS.Mount( '', '', 0 ) ;
    end ;
    if( UE <> nil ) then
    begin
        Show_Error( UE ) ;
        exit ;
    end ;

Next we prepare the target file system for the copy operation. If the file system is dirty, we notify the user and do a rebuild. If there is a problem, we let the user know and stop.

    try
        S2 := '' ;
        Info := FS.Get_File_Info( '\uos', 0 ) ;
        if( FS.Last_Error <> nil ) then // No UOS folder
        begin
            fillchar( Info, sizeof( Info ), 0 ) ;
            Info.Flags := FAF_PERMANENT or FAF_SYSTEM or FAF_DIRECTORY ;
            UE := FS.Create_File( '\uos', Info ) ;
            if( UE <> nil ) then // Could not create UOS
            begin
                Show_Error( UE ) ;
                S2 := 'NO' ;
            end ;
        end ;
        if( S2 <> 'NO' ) then
        begin
            Info := FS.Get_File_Info( PChar( '\uos\' + S ), 0 ) ;
            if( FS.Last_Error = nil ) then // Matching install already there
            begin
                Output( 'There is already an install with that name.  Do you wish to overwrite it <NO>? ' ) ;
                S2 := Input( 'NO' ) ;
                case( Process_Input( S2, S1, True ) ) of
                    1 : begin
                            exit ;
                        end ;
                    2 : begin
                            S2 := 'NO' ;
                        end ;
                end ;
            end else
            begin
                fillchar( Info, sizeof( Info ), 0 ) ;
                Info.Flags := FAF_PERMANENT or FAF_SYSTEM or FAF_DIRECTORY ;
                UE := FS.Create_File( PChar( '\uos\' + S ), Info ) ;
                if( UE <> nil ) then // Could not create new install folder
                begin
                    Show_Error( UE ) ;
                    S2 := 'NO' ;
                end ;
            end ; // if( FS.Last_Error = nil )
        end ; // if( S2 <> 'NO' )
    finally
        FS.Dismount ;
        FS.Free ;
        MStore.Free ;
    end ;
    if( S2 = 'NO' ) then
    begin
        goto Ask_Name ;
    end ;

Next we create the directory. If it already exists, we ask the user if they want to overwrite it. If not, we go back and prompt them for a new name.

Ask_Default:
    if( S3 = 'YES' ) then
    begin
        Output( 'Make this the default install <YES>? ' ) ;
        S3 := Input( 'YES' ) ;
        case( Process_Input( S2, S1, True ) ) of
            1 : begin
                    exit ;
                end ;
            2 : begin
                    goto Ask_Name ;
                end ;
        end ;
    end ;

Next, we ask the user if he wants to make the new copy of UOS to be the default boot version.

    // Do the install...
    MStore := TUOS_Managed_Store.Create ;
    MStore.Store := Store ;
    FS := TUOS_Native_File_System.Create ;
    FS.Store := MStore ;
    try
        FS.Mount( '', '', 0 ) ;

        // Find source directory...
        S2 := '\uos\installed.sys' ;
        Info := FS.Get_File_Info( PChar( S2 ), 0 ) ;
        if( FS.Last_Error <> nil ) then
        begin
            S2 := '\uos\default.sys' ;
            Info := FS.Get_File_Info( PChar( S2 ), 0 ) ;
        end ;
        if( FS.Last_Error <> nil ) then // No installed/default UOS
        begin
            // Find first directory in UOS...
            if(
                ( FS.Last_Error.Get_Error <> UOS_File_System_File_Not_Found )
                and
                ( FS.Last_Error.Get_Error <> UOS_File_System_Path_Not_Found )
              ) then
            begin
                Show_Error( FS.Last_Error ) ;
                exit ;
            end ;
            S1 := FS.Lookup( PChar( '\uos\*.*' ), Context ) ;
            try
                while( True ) do
                begin
                    if( ( Context = nil ) or ( S1 = '' ) ) then
                    begin
                        Output( 'Not an install media' + CRLF ) ;
                        exit ;
                    end ;
                    Info := FS.Get_File_Info( PChar( '\uos\' + S1 ), 0 ) ;
                    if( ( Info.Flags and FAF_DIRECTORY ) <> 0 ) then
                    begin
                        break ;
                    end ;
                    S1 := FS.Lookup( PChar( '' ), Context ) ; // Find next
                end ;
            finally
                FS.Clear_Context( Context ) ;
            end ;
        end else
        begin
            UF := FS.Get_File( PChar( '\uos\' + S2 ) ) ;
            if( UF = nil ) then
            begin
                if( FS.Last_Error <> nil ) then
                begin
                    Show_Error( FS.Last_Error ) ;
                end else
                begin
                    Output( 'Invalid install media' + CRLF ) ;
                end ;
                exit ;
            end ;
            try
                Info := FS.Get_File_Info( PChar( '\uos\' + S2 ), 0 ) ;
                setlength( S1, Info.EOF ) ;
                UF.Read( 0, 0, Info.EOF, PChar( S1 )[ 0 ] ) ;
            finally
                UF.Free ;
            end ;
        end ;
        // S1 now contains the source directory to copy

Next we determine the source directory that we will be copying from. We look for an installed.sys or default.sys link. If found, we use that. Otherwise, we use the first subdirectory found in the source store's UOS directory.

    // Copy the files...
    Output( 'Copying files' ) ;
    S2 := FS.Lookup( PChar( '\uos\' + S1 + '\*.*' ), Context ) ;
    try
        while( Context <> nil ) do
        begin
            Info := FS.Get_File_Info( PChar( '\uos\' + S1 + '\' + S2 ), 0 ) ;
            UF := FS.Get_File( PChar( '\uos\' + S1 + '\' + S2 ) ) ;
            if( UF = nil ) then
            begin
                if( FS.Last_Error <> nil ) then
                begin
                    Show_Error( FS.Last_Error ) ;
                end else
                begin
                    Output( 'Invalid install media' + CRLF ) ;
                end ;
                exit ;
            end ;
            try
                UE := FS.Create_File( PChar( '\uos\' + S + '\' + S2 ), Info ) ;
                if( UE <> nil ) then
                begin
                    Show_Error( UE ) ;
                    exit ;
                end ;
                TF := FS.Get_File( PChar( '\uos\' + S + '\' + S2 ) ) ;
                if( TF = nil ) then
                begin
                    if( FS.Last_Error <> nil ) then
                    begin
                        Show_Error( FS.Last_Error ) ;
                    end else
                    begin
                        Output( 'Invalid install media' + CRLF ) ;
                    end ;
                    exit ;
                end ;
                try
                    P := 0 ;
                    Count := UF.Read( 0, P, sizeof( Buff ), Buff ) ;
                    while( Count > 0 ) do
                    begin
                        if( TF.Write( 0, P, Count, Buff ) <> Count ) then
                        begin
                            Show_Error( FS.Last_Error ) ;
                            exit ;
                        end ;
                        P := P + Count ;
                        Count := UF.Read( 0, P, sizeof( Buff ), Buff ) ;
                    end ;
                    Output( '.' ) ;
                    TF.Contiguous := True ;
                    Output( '.' ) ;
                finally
                    TF.Detach ;
                end ;
            finally
                UF.Detach ;
            end ;
            S2 := FS.Lookup( PChar( '' ), Context ) ; // Find next
        end ;
    finally
        FS.Clear_Context( Context ) ;
    end ;

Next, we do the actual copy operation. We iterate through the source directory. For each file, we create it in the destination directory, copy the contents, and set the file to contiguous.

        // Set this as the default install...
        Output_Line( '' ) ;
        Output_Line( 'Hooking uos...' ) ;
        if( ( S3 = '' ) or ( copy( S3, 1, 1 ) = 'Y' ) ) then
        begin
            fillchar( Info, sizeof( Info ), 0 ) ;
            Info.Flags := FAF_LINK ;
            UE := FS.Create_File( '\uos\installed.sys', Info ) ;
            if( UE <> nil ) then // Could not create installed.sys
            begin
                Show_Error( UE ) ;
                Output_Line( 'Cannot set installed O/S - Continuing...' ) ;
            end else
            begin
                Fil := FS.Get_File( '\uos\installed.sys' ) ;
                S2 := '\uos\' + S ;
                Fil.Write( 0, 0, length( S2 ), PChar( S2 )[ 0 ] ) ;
                if( Fil.Last_Error <> nil ) then
                begin
                    Show_Error( Fil.Last_Error ) ;
                    Output_Line( 'Cannot set installed O/S - Continuing...' ) ;
                end ;
                Fil.Detach ;
            end ;
            UE := FS.Create_File( '\uos\default.sys', Info ) ;
            if( UE <> nil ) then // Could not create installed.sys
            begin
                Show_Error( UE ) ;
                Output_Line( 'Cannot set default O/S - Continuing...' ) ;
            end else
            begin
                Fil := FS.Get_File( '\uos\default.sys' ) ;
                S2 := '\uos\' + S ;
                Fil.Write( 0, 0, length( S2 ), PChar( S2 )[ 0 ] ) ;
                if( Fil.Last_Error <> nil ) then
                begin
                    Show_Error( Fil.Last_Error ) ;
                    Output_Line( 'Cannot set default O/S - Continuing...' ) ;
                end ;
                Fil.Detach ;
            end ;

            // Hook Init and the OS...
            try
                // Get existing boot block...
                Buffer := allocmem( MStore.Min_Storage ) ;
                Store.Read_Data( Buffer[ 0 ], 0, Store.Min_Storage, UE ) ;
                if( UE <> nil ) then
                begin
                    Show_Error( UE ) ;
                    Output_Line( 'Cannot make store bootable' ) ;
                    exit ;
                end ;

                // Update boot block...
                HAL.Write_Bootstrap( Buffer ) ;
                Buffer[ $1FE ] := #$55 ;
                Buffer[ $1FF ] := #$AA ;
                move( Buffer[ 0 ], Boot_Header, sizeof( Boot_Header ) ) ;
                if( Get_File( 'hal.sys' ) ) then
                begin
                    exit ;
                end ;
                if( ( FS.Last_Error = nil ) and ( ( Info.Flags and FAF_CONTIGUOUS ) <> 0 ) ) then
                begin // File exists and is contiguous
                    Boot_Header.HAL_Position := Info.Location ;
                    Boot_Header.HAL_Length := Info.Size ;
                end ;
                if( Get_File( 'uosfs.sys' ) ) then
                begin
                    exit ;
                end ;
                if( FS.Last_Error = nil ) then
                begin // File exists and is contiguous
                    Boot_Header.FS_Position := Info.Location ;
                    Boot_Header.FS_Length := Info.Size ;
                end ;
                if( Get_File( 'init.sys' ) ) then
                begin
                    exit ;
                end ;
                if( FS.Last_Error = nil ) then
                begin // File exists and is contiguous
                    Boot_Header.Bootstrap_Position := Info.Location ;
                    Boot_Header.Bootstrap_Length := Info.Size ;
                end ;

                move( Boot_Header, Buffer[ 0 ], sizeof( Boot_Header ) ) ;
                Store.Write_Data( Buffer[ 0 ], 0, MStore.Min_Storage, UE ) ;
                if( UE <> nil ) then
                begin
                    Show_Error( UE ) ;
                    Output_Line( 'Cannot make store bootable' ) ;
                    exit ;
                end ;

                // Get existing store header...
                Store.Read_Data( Buffer[ 0 ], Boot_Header.Header, Store.Min_Storage, UE ) ;
                if( UE <> nil ) then
                begin
                    Show_Error( UE ) ;
                    Output_Line( 'Cannot make uos bootable' ) ;
                    exit ;
                end ;
                move( Buffer[ 0 ], Store_Header, sizeof( Store_Header ) ) ;
                if( Get_File( 'kernel.sys' ) ) then
                begin
                    exit ;
                end ;
                if( FS.Last_Error = nil ) then
                begin // File exists and is contiguous
                    Store_Header.OS_Position := Info.Location ;
                    Store_Header.OS_Length := Info.Size ;
                end ;
                if( Get_File( 'mmc.sys' ) ) then
                begin
                    exit ;
                end ;
                if( FS.Last_Error = nil ) then
                begin // File exists and is contiguous
                    Store_Header.MMC_Position := Info.Location ;
                    Store_Header.MMC_Length := Info.Size ;
                end ;
                if( Get_File( 'hmc.sys' ) ) then
                begin
                    exit ;
                end ;
                if( FS.Last_Error = nil ) then
                begin // File exists and is contiguous
                    Store_Header.HMC_Position := Info.Location ;
                    Store_Header.HMC_Length := Info.Size ;
                end ;

                move( Store_Header, Buffer[ 0 ], sizeof( Store_Header ) ) ;
                Store.Write_Data( Buffer[ 0 ], Boot_Header.Header, Store.Min_Storage, UE ) ;
                if( UE <> nil ) then
                begin
                    Show_Error( UE ) ;
                    Output_Line( 'Cannot make store bootable' ) ;
                    exit ;
                end ;
            finally
                freemem( Buffer ) ;
            end ;
        end ; // if( copy( S3, 1, 1 ) = 'Y' )
    finally
        FS.Dismount ;
        FS.Free ;
        MStore.Free ;
    end ;
end ; // Install

Finally, we hook UOS so it can be booted. This is a lot of lines of code, but it is straight-forward. Each of the required component files is located so that we have the position, and length, of the data. Then we write it to the boot block or the file system header. The local Get_File routine double-checks that the files are contiguous. If they aren't, we try to force them to be contiguous. If that fails, then we abort the process. Here is the code for Get_File:
    function Get_File( const Name : string ) : boolean ;

    begin
        Result := False ;
        Info := FS.Get_File_Info( PChar( '\uos\' + S + '\' + Name ), 0 ) ;
        if( ( FS.Last_Error = nil ) and ( ( Info.Flags and FAF_CONTIGUOUS ) = 0 ) ) then
        begin // File exists and isn't contiguous
            // Make file contiguous...
            Info.Flags := Info.Flags or FAF_CONTIGUOUS ;
            fillchar( IFlags, sizeof( IFlags ), 0 ) ;
            IFlags.Flags := -1 ;
            FS.Set_File_Info( PChar( '\uos\' + S + '\' + Name ), 0, Info, IFlags ) ; // Make contiguous
            if( FS.Last_Error <> nil ) then
            begin
                Info.Flags := 0 ; // Don't update HAL information
            end else
            begin
                Info := FS.Get_File_Info( PChar( '\uos\' + S + '\' + Name ), 0 ) ;
            end ;
        end ;
    end ; // .Get_File

UOS Startup
We briefly touched on starting UOS from Init a few articles ago. We left off with the creation of the startup parameters buffer to pass to UOS. We have extended this routine to do more. First, we allow a name to be passed to the Start function which is the name (directory) of the UOS installation to start up. Second, we try to write the parameters to a file in the UOS directory, named startup.dat. If that fails (for instance, due to a read-only disk), then we use the HAL to pass the information. Here's the rest of the code for the Start routine:

    // Handle optional install selection...
    HAL_Position := 0 ;
    HAL_Length := 0 ;
    UOS_Position := 0 ;
    UOS_Length := 0 ;
    Pass_Installation_Info := False ;
    if( S1 <> '' ) then
    begin
        MStore := TUOS_Managed_Store.Create ;
        MStore.Store := _Store ;
        FS := TUOS_Native_File_System.Create ;
        FS.Store := MStore ;
        try
            FS.Mount( '', '', 0 ) ;
            UE := FS.Last_Error ;
            if( UE <> nil ) then
            begin
                if( UE.Get_Error = UOS_File_System_Is_Dirty ) then
                begin
                    Output_Line( 'Disk is dirty - rebuilding...' ) ;
                    FS.Rebuild ;
                end ;
                FS.Mount( '', '', 0 ) ;
                UE := FS.Last_Error ;
                if( UE <> nil ) then
                begin
                    Show_Error( UE ) ;
                    Output_Line( 'Cannot start UOS' ) ;
                    exit ;
                end ;
            end ;
            FInfo := FS.Get_File_Info( PChar( '\uos\' + S1 ), 0 ) ;
            if( FS.Last_Error <> nil ) then // Folder not found
            begin
                Output_Line( 'Installation not found' ) ;
                exit ;
            end ;
            if( _Store.Read_Only ) then // Cannot set installed.sys in this case
            begin
                Pass_Installation_Info := True ;
            end else
            begin
                fillchar( Info, sizeof( Info ), 0 ) ;
                FInfo.Flags := FAF_LINK ;
                UE := FS.Create_File( '\uos\installed.sys', FInfo ) ;
                if( UE <> nil ) then // Could not create installed.sys
                begin
                    Pass_Installation_Info := True ;
                end else
                begin
                    UF := FS.Get_File( '\uos\installed.sys' ) ;
                    S2 := '\uos\' + S1 ;
                    UF.Write( 0, 0, length( S2 ), PChar( S2 )[ 0 ] ) ;
                    if( UF.Last_Error <> nil ) then
                    begin
                        Pass_Installation_Info := True ;
                    end ;
                    UF.Detach ;
                end ;
            end ;
            FInfo := FS.Get_File_Info( PChar( '\uos\' + S1 + '\hal.sys' ), 0 ) ;
            if( FS.Last_Error <> nil ) then // Not found
            begin
                Output_Line( 'Installation HAL not found' ) ;
                exit ;
            end ;
            HAL_Position := FInfo.Location ;
            HAL_Length := FInfo.Size ;
            FInfo := FS.Get_File_Info( PChar( '\uos\' + S1 + '\kernel.sys' ), 0 ) ;
            if( FS.Last_Error <> nil ) then // Not found
            begin
                Output_Line( 'Installation kernel not found' ) ;
                exit ;
            end ;
            UOS_Position := FInfo.Location ;
            UOS_Length := FInfo.Size ;
        finally
            FS.Dismount ;
            MStore.Store := nil ;
            FS.Free ;
            MStore.Free ;
        end ;
    end ; // if( S1 <> '' )

If a installation name is passed in, we mount the file system (if dirty, we rebuild it). Next we ensure that the requested installation name exists in the root UOS directory. If not, we show an error and exit. Pass_Installation_Info is a flag that is set if we have to pass information via the HAL's Set_Configuration method. The flag is set if we cannot hook the requested version of UOS for any reason. These reasons include such things as the store being read-only, or any error that occurs in the process of creating a new installed.sys link. Next we obtain the location and size of hal.sys and kernel.sys for later use in the routine. Finally, we dismount and free the file system.

    // Pass parameters to UOS...
    Configuration_Set := True ;
    if( not _Store.Read_Only ) then
    begin
        MStore := TUOS_Managed_Store.Create ;
        MStore.Store := _Store ;
        FS := TUOS_Native_File_System.Create ;
        FS.Store := MStore ;
        try
            FS.Mount( '', '', 0 ) ;
            UE := FS.Last_Error ;
            if( UE <> nil ) then
            begin
                if( UE.Get_Error = UOS_File_System_Is_Dirty ) then
                begin
                    Output_Line( 'Disk is dirty - rebuilding...' ) ;
                    FS.Rebuild ;
                end ;
                FS.Mount( '', '', 0 ) ;
                UE := FS.Last_Error ;
                if( UE <> nil ) then
                begin
                    Show_Error( UE ) ;
                    Output_Line( 'Cannot start UOS' ) ;
                    exit ;
                end ;
            end ;
            fillchar( FInfo, sizeof( FInfo ), 0 ) ;
            FInfo.Flags := FAF_SYSTEM ;
            FInfo.Creation := HAL.Timestamp ;
            FInfo.Last_Modified := FInfo.Creation ;
            UE := FS.Create_File( '\uos\startup.dat', FInfo ) ;
            if( UE <> nil ) then
            begin
                Configuration_Set := False ;
            end else
            begin
                Fil := FS.Get_File( '\uos\startup.dat' ) ;
                Fil.Write( 0, 0, length( S ), PChar( S )[ 0 ] ) ;
                Fil.Free ;
            end ;
        finally
            FS.Dismount ;
            FS.Free ;
            MStore.Free ;
        end ;
    end ; // if( not _Store.Read_Only )

If the store is writable, we mount the file system and attempt to create the startup.dat file. We write the installation parameter string to it. If we succeed, we set the Configuration_Set flag to true.

    if( not Configuration_Set ) then
    begin
        if( S <> '' ) then
        begin
            S := Encode_Parameters( UOS_Config_Boot_Device, Boot_Device.Device_Type, Boot_Device.Controller,
                Boot_Device.Device_Unit, 3 ) + S ;
        end ;
        if( Pass_Installation_Info ) then
        begin
            S := S + Encode_Parameters( UOS_Config_Installation, length( S1 ), 0, 0 , 1 ) + S1 ;
        end ;
        if( HAL.Set_Configuration( PChar( S ), length( S ) ) <> length( S ) ) then
        begin
            Output_Line( 'Failed to set configuration' ) ;
            exit ;
        end ;
    end ;

If Configuration_Set is false, we call the HAL.Set_Configuration. In the case where we are starting an installation other than the default (Pass_Installation_Info is set), we add a new record to the installation parameters. The UOS_Config_Installation record type is followed by a length value, and then a string that contains the name of the installation directory. It is preferable to write the data to the boot disk since the space available in Set_Configuration may not be sufficient. But, if we can't, we use the HAL to do it instead. If we can't pass the parameters either way, we abort with an error.

    // Load Kernel/HAL...
    if( HAL_Position = 0 ) then // No installation specified - use default
    begin
        Buffer := allocmem( _Store.Min_Storage ) ;
        _Store.Read_Data( Buffer[ 0 ], 0, _Store.Min_Storage, UE ) ;
        if( UE <> nil ) then
        begin
            Show_Error( UE ) ;
            Output_Line( 'Cannot start UOS' ) ;
            exit ;
        end ;
        move( Buffer[ 0 ], Bootstrap, sizeof( Bootstrap ) ) ;
        HAL_Position := Bootstrap.HAL_Position ;
        HAL_Length := Bootstrap.HAL_Length ;

        _Store.Read_Data( Buffer[ 0 ], Bootstrap.Header, _Store.Min_Storage, UE ) ;
        if( UE <> nil ) then
        begin
            Show_Error( UE ) ;
            Output_Line( 'Cannot start UOS' ) ;
            exit ;
        end ;
        move( Buffer[ 0 ], FSH, sizeof( FSH ) ) ;
        UOS_Position := FSH.OS_Position ;
        UOS_Length := FSH.OS_Length ;
    end ;
    H := THAL( Get_Image( _Store, HAL_Position, HAL_Length, 3 ) ) ;
    if( H = nil ) then
    begin
        Output_Line( 'Cannot load HAL' ) ;
        exit ;
    end ;
    H.Allocate_At( int64( H ), HAL_Length ) ;
    K := TUOS_Kernel( Get_Image( _Store, UOS_Position, UOS_Length, 4 ) ) ;
    if( K = nil ) then
    begin
        Output_Line( 'Cannot load UOS' ) ;
        H.Free ;
        exit ;
    end ;
    H.Allocate_At( int64( K ), UOS_Length ) ;
    K.Startup( H ) ;

Now that we have an install process that hooks UOS, we can boot UOS from Init. If an installation name is passed to the routine, we will already have the HAL and Kernel positions and lengths. Otherwise, we get the pointer and length of the HAL from the boot block. Then we get the pointer and length of the Kernel from the file system header. We use Get_Image to load the components, aborting if there is a problem anywhere along the line. Note that our Get_Image function asks the current HAL for buffers to hold the code we load from the store. But once we start up the new HAL and Kernel, the old HAL's allocations are lost, so we tell the new HAL to allocate the memory that the new HAL and Kernel occupy so that it isn't overwritten by something later. What about the memory already occupied by Init and the current HAL? Once we pass control to the new Kernel/HAL, their memory is considered unused and available for other uses. They are essentially ignored as if they were never there. Once the Kernel is in control, it is charge and nothing that went before matters (except what we pass as parameters to it as described above). Once we call the Startup routine, our job as Init is done.

Our Get_Image function is similar to the one we introduced in the last article in the Kernel code.

function Get_Image( S : TCOM_Store64 ; Root, Len : int64 ; O6 : byte ) : TObject ;

var Address : int64 ;
    P : pointer ;
    UEC : TUnified_Exception ;

begin
    Result := nil ;
    if( ( Root <> 0 ) and ( Len > 0 ) and ( ( Root and 1 ) = 0 ) and ( ( Len and 1 ) = 0 ) ) then
    begin
        // Round length up to full sector size
        Len := ( Len + S.Min_Storage - 1 ) and ( not ( S.Min_Storage - 1 ) ) ;

        // Allocate storage and read into memory...
        Address := HAL.Allocate( 0, Len ) ;
        if( Address <> 0 ) then
        begin
            P := pointer( Address ) ;
            S.Read_Data( P^, Root, Len, UEC ) ;
            if( UEC = nil ) then // Read into memory okay
            begin
                PChar( Address )[ 6 ] := char( O6 ) ;
                Result := TObject( HAL.Execute( Address ) ) ;
            end ;
        end ;
    end ;
end ; // Get_Image

You can revist the previous article if you don't recognize anything in the above code.

We will look at support code in Init for managing multiple UOS installations in a future article. If you've been paying close attention, you may notice a potential problem in our code. Namely, although we use the hal.sys and kernel.sys specific to a requested start of a non-default copy of UOS, we are still loading the MMC.sys and HMC.sys from the hooked version of UOS. In a future article, we will discuss how we handle this situation with hot-swappable components.

In the next article, we will return to the Kernel startup and look at the MMC component.