Table of Contents

Windows XP network install

This is a useful technique if you don't have an XP installation CD and need to install Windows XP on an old system which supports PXE. I've found that USB installation is unreliable.

How everything fits together

There are quite a few moving parts in the Windows XP network install process:

Preparation

Download the following:

Samba setup

Create the share directory and copy the Windows XP CD contents into it:

sudo zfs create zpool/risinstall
cd /mnt/zfs/risinstall
 
mkdir winxp_32bit
cd winxp_32bit
 
# Unpack the CD. Can also use 'cp -r /media/WINXP/* .'
7z x /media/WINXP.ISO
 
# Fix permissions and convert filenames to lower case
find -type d -exec chmod 755 {} \;
find -type f -exec chmod 644 {} \;
find . -depth -exec rename 's!([^/]*\Z)!lc($1)!e' {} +
 
# Change names of files which stage-0 TFTP requests as uppercase, back to uppercase
for i in KDCOM.DL_ BOOTVID.dl_ SETUPREG.HI_ SETUPREG.HIV SPDDLANG.SY_ WMILIB.SY_ OPRGHDLR.SY_ 1394BUS.SY_ PCIIDEX.SY_ USBPORT.SY_ USBD.SY_ HIDCLASS.SY_ HIDPARSE.SY_ VIDEOPRT.SY_ SCSIPORT.SY_ CLASSPNP.SY_ TDI.SY_ ; do
	mv i386/`echo $i | tr '[:upper:]' '[:lower:]'` i386/$i
done
 
# Make the system files needed for Setup executable (required by Samba 4, see manpage https://www.samba.org/samba/docs/current/man-html/smb.conf.5.html#ACLALLOWEXECUTEALWAYS)
chmod 755 i386/system32/* i386/*.dll i386/*.exe i386/*.com

Edit /etc/samba/smb.conf to add the share:

[risinstall]
        comment    = Unattended Windows XP install
        path       = /mnt/zfs/risinstall
        guest ok   = yes
        browseable = no
        writeable  = yes
        locking    = no

Restart Samba (sudo service smbd restart).

DNSmasq configuration (advertising a remote TFTP server)

Add these lines to /etc/dnsmasq.d/pxe.conf, replacing the XX'ed MAC address with the target PC's MAC address, and TFTP-SERVER with the hostname or IP address of the TFTP server.

; Change the MAC address to that of the PC you want to netboot
dhcp-host=XX:XX:XX:XX:XX:XX,set:netbootwxp

; Windows XP Netinstall for 
dhcp-boot=tag:netbootwxp,pxelinux.0,TFTP-SERVER,TFTP-SERVER

TFTP server setup

Install the TFTP server, PXELinux and Syslinux:

sudo apt install pxelinux syslinux-efi tftpd-hpa

Windows XP requests files from TFTP using Windows naming conventions, so we need to use a TFTP map file to convert these names to Unix

# Convert Windows naming conventions to Linux (backslashes to forward-slashes)
rg \\ /
 
# Convert non-absolute files to absolute.
r       ^[^/]           /srv/tftp/\0
 
# Convert relative paths to their absolute position
r       ^/syslinux/             /srv/tftp\0
r       ^/pxelinux.cfg/         /srv/tftp\0
r       ^/winxp_32bit/          /srv/tftp\0
 
# Add the remote IP address as a folder on the front of all requests.
# This can be useful for sending different winnt.sif's to different machines.
#r ^ \i/

And edit /etc/default/tftpd-hpa to set:

# /etc/default/tftpd-hpa

TFTP_USERNAME="tftp"
TFTP_DIRECTORY="/srv/tftp"
TFTP_ADDRESS=":69"
#TFTP_OPTIONS="--secure -vvv -m /etc/tftpd-hpa.rules"
TFTP_OPTIONS="-vvv -m /etc/tftpd-hpa.rules"

Note that secure mode is disabled - this is deliberate. In secure mode, tftpd-hpa runs in a chroot inside the TFTP directory, which prevents it from following symlinks outside the TFTP root.

Next set up PXELinux:

cd /srv/tftp
 
sudo cp /usr/lib/PXELINUX/pxelinux.0 .
sudo cp /usr/lib/SYSLINUX.EFI/efi64/syslinux.efi .
sudo mkdir syslinux
for i in ldlinux.c32 libutil.c32 menu.c32; do sudo cp /usr/lib/syslinux/modules/bios/$i syslinux/; done
 
mkdir pxelinux.cfg
cat <<EOF >pxelinux.cfg/default
timeout 0
prompt 1
ui menu.c32
 
label xp32_netinst
  kernel startrom.0

Prepare the Windows XP installation files:

cp /mnt/zfs/risinstall/winxp_32bit/i386/ntdetect.com .
cp /mnt/zfs/risinstall/winxp_32bit/i386/setupldr.bin ntldr
 
# Decompress startrom.n12 as startrom.0
7z x /mnt/zfs/risinstall/winxp_32bit/i386/startrom.n1_
mv startrom.n12 startrom.0
 
# Convert SETUPLDR.BIN into NTLDR
fixloader.py ntldr

Appendix: fixloader.py, Python 3 version

fixloader.py
#!/usr/bin/env python3
# -*- Mode: Python; tab-width: 4 -*-
#
# Fix for setuploader
#
# Copyright (C) 2005-2006 Gianluigi Tiesi <sherpya@netfarm.it>
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by the
# Free Software Foundation; either version 2, or (at your option) any later
# version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY
# or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
# for more details.
# ======================================================================
 
from sys import argv, exit as sys_exit
 
if __name__ == '__main__':
    if len(argv) < 2:
        print('Usage: fixloader.py ntldr')
        sys_exit()
 
    data = open(argv[1], 'rb').read()
 
    if data.find(b'setupldr.exe')==-1:
        print('Wrong file')
        sys_exit()
 
    if data[:2] == b'MZ':
        print('Loader already fixed')
        sys_exit()
 
    data = b'MZ' + data.split(b'MZ', 1).pop()
 
    open(argv[1], 'wb').write(data)
    print('Loader fixed')

Creating the setup information file

Create a file called winnt.sif in the TFTP root. The one below is an example.

Replace FILESERVER with the SMB name of the file server, and RISINSTALL with the share name.

OriSrc should point to the i386 directory as it's used if Windows needs extra files after it's been installed.

SetupSourceDevice is the SMB path in the form of the device, and should point to what is effectively the root directory, one level up from the i386 directory.

[data]
floppyless = "1"
msdosinitiated = "1"
; Needed for second stage -- this is used as the installation path for Windows itself (if it needs things later)
OriSrc = "\\FILESERVER\RISINSTALL\winxp_32bit\i386"
OriTyp = "4"
LocalSourceOnCD = 1
DisableAdminAccountOnDomainJoin = 1
 
; ;;;;;;;;;;;;;
; Copy the [data] section from your nLite i386/WINNT.SIF file into the area below.
; ;;;;;;;;;;;;;
AutomaticUpdates="Yes"
Autopartition=0
MsDosInitiated=0
UnattendedInstall="Yes"
; ;;;;;;;;;;;;;
; End nLite area
; ;;;;;;;;;;;;;
 
[SetupData]
OsLoadOptions = "/fastdetect"
; Needed for first stage
SetupSourceDevice="\Device\LanmanRedirector\FILESERVER\RISInstall\winxp_32bit"
 
[RemoteInstall]
; Avoid automatic format/repartition
Repartition = No
UseWholeDisk = No
 
[UserData]
; Samba will see this as an underscore
ComputerName = *
ProductID=XXXXX-XXXXX-XXXXX-XXXXX-XXXXX
 
 
; ;;;;;;;;;;;;;
; Copy your nLite i386/WINNT.SIF file (except the [data] section) below.
; ;;;;;;;;;;;;;

Network drivers and BINL

BINL is used to convert a network card identity (PCI ID) into a device driver name. It requires a server on the local network, and a directory of INF files. The SYS file is retrieved via TFTP.

If you need an Ethernet driver which isn't included with Windows XP, download and unpack it. For this example I'll be using the Marvell Yukon driver.

First we create the BINL support directories and unpack the Windows XP standard driver descriptions:

mkdir -p /mnt/zfs/risinstall/binl/tmp
cd /mnt/zfs/risinstall/binl/tmp
 
cabextract /mnt/zfs/risinstall/winxp_32bit/I386/*.IN_
cabextract /mnt/zfs/risinstall/winxp_32bit/I386/DRIVER.CAB

Next we copy the Marvell Yukon INF file into the BINL input directory, and the driver into the Windows i386 directory:

cp /mnt/zfs/risinstall/Drivers/yukon/XP32/*.inf /mnt/zfs/risinstall/binl/tmp
cp /mnt/zfs/risinstall/Drivers/yukon/XP32/*.sys /mnt/zfs/risinstall/winxp_32bit/i386

And the same for the Realtek RTL811E drivers:

cp /mnt/zfs/risinstall/Drivers/rtl8111e/WINXP/*.inf /mnt/zfs/risinstall/binl/tmp
cp /mnt/zfs/risinstall/Drivers/rtl8111e/WINXP/*.sys /mnt/zfs/risinstall/winxp_32bit/i386

Now run nLite and use it to integrate the network drivers into the installer. If you don't do this, then the network drivers will only be used for the setup process, and will not actually be installed on the system.

Finally we create the BINL cache and start the server:

# Create BINL cache
cd /mnt/zfs/risinstall/binl
../ris-linux-0.4/infparser.py tmp

# Start BINL server
gcc -o binlsrv ../ris-linux-0.4/binlsrv.c
./binlsrv

# Alternatively use the Python BINL server
../ris-linux-0.4/binlsrv.py

I've found that the Python BINL server doesn't always find the driver correctly (e.g. on the Intel D525MW), but the C version is quite reliable.

Booting the target system

Now the TFTP, SMB and BINL servers should be running.

Configure the target system for PXE network boot, and trigger a netboot. On the Asus Rampage Formula system I used for testing, this is done by enabling the LAN Boot ROM, then hitting F8 for the boot menu, and selecting the Marvell Yukon PXE entry.

If you see NT_STATUS_ACCESS_DENIED errors from Samba (or STATUS_ACCESS_DENIED in Wireshark decoded SMB protocol traces), then check whether the file being requested is executable. If so, and the client expects to execute it, then it may need to be chmod +x'd.

Post-installation steps

This more or less boils down to installing the applications and drivers you need. I usually put these in a subdirectory of the RISInstall share directory, then run them over the network.

I've yet to find a good way to pre-install applications automatically. I imagine something could be done with a RunOnce modification in nLite, but that's something I need to look into.

The main applications you'll want to install are:

Next steps

nLite

nLite can be used to integrate service packs and drivers. Generally this is most useful for network and RAID/AHCI drivers as everything else can usually be loaded after the installation has completed.

Note that some filenames will need to be fixed after nLite has built the image:

Other files may need to be renamed; these will be visible when the setup is first started from the nLite'd image directory.

Useful changes to make with nLite

These are starting from a Windows XP SP3 CD.

Multiple Windows XP ''SIF'' files

Sometimes it's necessary to have several different winnt.sif files, to accommodate different deployment images.

The easiest way to achieve this is to modify the TFTP server rules file to remap requests for winnt.sif to winnt_\i.sif. This will select the SIF file based on the IP address of the requesting machine.

Sometimes it's preferable to have multiple images which can be selected from the menu. This can be achieved by patching the loaders, as documented by Jui-Nan Lin:

The modified loader is referenced in the PXELinux configuration as winxp.0. With the above modifications, the load order changes to:

winxp.0 (modified startrom) → XPLDR (modified ntldr) → ntdetect.wxp, winxp.sif → (install)

While this process works, it's imperfect:

Troubleshooting

Product Key is in WINNT.SIF, but Setup still asks for it -- or Product Key is rejected

Per KB950722 and this thread on MSFN, this is usually caused by slipstreaming an XP Service Pack into an installation under Windows Server 2003 or Windows 7.

To resolve this, you'll have to recreate your nLite-d install image using Windows XP.

Identifying image type (matching product keys)

Windows XP images generally require a product key for the specific type of Windows in use. There are two ways to identify the image type.

These both assume the image hasn't been monkeyed with: changing the Pid will generally not work to change a VLK installation into a Retail one, per the comments on this MyDigitalLife post.

Appendices

Appendix: Files requested over TFTP before starting Setup

These files are requested by the text-mode version of the Windows XP setup utility.

All these files are in the i386 directory.

txtsetup.si_
txtsetup.sif
biosinfo.in_
biosinfo.inf
drvmain.sd_
drvmain.sdb
migrate.in_
migrate.inf
unsupdrv.in_
unsupdrv.inf
halmacpi.dl_
ntkrnlmp.ex_
KDCOM.DL_
BOOTVID.dl_
SETUPREG.HI_
SETUPREG.HIV
vgaoem.fo_
c_1252.nl_
c_437.nl_
l_intl.nl_
c_1252.nl_
c_437.nl_
l_intl.nl_
setupdd.sy_
SPDDLANG.SY_
pci.sy_
acpi.sy_
WMILIB.SY_
isapnp.sy_
acpiec.sy_
OPRGHDLR.SY_
ohci1394.sy_
1394BUS.SY_
pcmcia.sy_
pciide.sy_
PCIIDEX.SY_
intelide.sy_
viaide.sy_
cmdide.sy_
toside.sy_
aliide.sy_
mountmgr.sy_
ftdisk.sy_
partmgr.sy_
fdc.sy_
dmload.sy_
dmio.sy_
sbp2port.sy_
lbrtfdc.sy_
usbehci.sy_
USBPORT.SY_
usbohci.sy_
usbuhci.sy_
usbhub.sy_
USBD.SY_
usbccgp.sy_
hidusb.sy_
HIDCLASS.SY_
HIDPARSE.SY_
serial.sy_
serenum.sy_
usbstor.sy_
vga.sy_
VIDEOPRT.SY_
i8042prt.sy_
kbdhid.sy_
kbdclass.sy_
SCSIPORT.SY_
cpqarray.sy_
atapi.sy_
aha154x.sy_
sparrow.sy_
symc810.sy_
aic78xx.sy_
i2omp.sy_
dac960nt.sy_
ql10wnt.sy_
amsint.sy_
asc.sy_
asc3550.sy_
mraid35x.sy_
ini910u.sy_
ql1240.sy_
aic78u2.sy_
symc8xx.sy_
sym_hi.sy_
sym_u3.sy_
asc3350p.sy_
abp480n5.sy_
cd20xrnt.sy_
ultra.sy_
adpu160m.sy_
dpti2o.sy_
ql1080.sy_
ql1280.sy_
ql12160.sy_
perc2.sy_
hpn.sy_
cbidf2k.sy_
dac2w2k.sy_
dmboot.sy_
cdrom.sy_
CLASSPNP.SY_
disk.sy_
sfloppy.sy_
ramdisk.sy_
ksecdd.sy_
ksecdd.sys
fastfat.sy_
ntfs.sy_
ntfs.sys
cdfs.sy_
ndis.sy_
ipsec.sy_
tcpip.sy_
TDI.SY_
ipnat.sy_
netbt.sy_
rdbss.sy_
mup.sy_
mrxsmb.sy_

Note that the network driver is also requested from TFTP, after its name has been obtained using BINL.

Appendix: TFTP remapping rules to convert filenames to lowercase

These are useful for debugging the stage-zero (TFTP bootstrapping) phase.

Source: https://www.syslinux.org/archives/2004-August/003883.html

Making them exclusive to WinXP is a bit tricky, see: https://www.syslinux.org/archives/2004-August/003884.html

rg      A               a               # lower case
rg      B               b               # lower case
rg      C               c               # lower case
rg      D               d               # lower case
rg      E               e               # lower case
rg      F               f               # lower case
rg      G               g               # lower case
rg      H               h               # lower case
rg      I               i               # lower case
rg      J               j               # lower case
rg      K               k               # lower case
rg      L               l               # lower case
rg      M               m               # lower case
rg      N               n               # lower case
rg      O               o               # lower case
rg      P               p               # lower case
rg      Q               q               # lower case
rg      R               r               # lower case
rg      S               s               # lower case
rg      T               t               # lower case
rg      U               u               # lower case
rg      V               v               # lower case
rg      W               w               # lower case
rg      X               x               # lower case
rg      Y               y               # lower case
rg      Z               z               # lower case