Up to the point where I get stuck in AccessBT
This commit is contained in:
parent
9d4022b833
commit
ebe262e429
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>URL</key>
|
||||
<string>http://mirror.informatimago.com/next/developer.apple.com/documentation/mac/Devices/Devices-13.html</string>
|
||||
</dict>
|
||||
</plist>
|
684
NetBoot.py
684
NetBoot.py
|
@ -4,8 +4,464 @@
|
|||
# Adapted from: http://chaos.weblogs.us/archives/164
|
||||
|
||||
import socket
|
||||
import os
|
||||
from os import path
|
||||
import struct
|
||||
|
||||
ANY = "0.0.0.0"
|
||||
import time
|
||||
|
||||
|
||||
|
||||
|
||||
# ; the below code might come in handy later on, when we support executable boot blocks.
|
||||
# CodeToUncorruptTheBootBlocks:
|
||||
# move.l (SP),A1
|
||||
# sub.l #8,A1 ; What will be the start address of the boot blocks??
|
||||
|
||||
# lea BigDiskImage,A0
|
||||
# move.l #1024,D0
|
||||
# dc.w 0xA02E ; BlockMove
|
||||
|
||||
# sub.l #6,(SP) ; Re-run the code that called us; it will be real boot blocks now!
|
||||
# rts
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
my_unique_ltoudp_id = b'El' + (os.getpid() & 0xFFFF).to_bytes(2, 'big')
|
||||
|
||||
|
||||
|
||||
disk_image = open(path.join(path.dirname(path.abspath(__file__)), 'systools607.dsk'), 'rb').read()
|
||||
|
||||
|
||||
|
||||
|
||||
def assemble(the_code):
|
||||
with open('/tmp/vasm.bootblocks', 'w') as f:
|
||||
f.write(the_code)
|
||||
|
||||
assembler = path.join(path.dirname(path.abspath(__file__)), 'vasm-1/vasmm68k_mot')
|
||||
os.system(f'{assembler} -quiet -Fbin -pic -o /tmp/vasm.bootblocks.bin /tmp/vasm.bootblocks')
|
||||
|
||||
with open('/tmp/vasm.bootblocks.bin', 'rb') as f:
|
||||
return f.read()
|
||||
|
||||
|
||||
|
||||
|
||||
# typedef short (*j_code)( short command, # SP+4 (sign-extend to long)
|
||||
# DGlobals *g, # SP+8
|
||||
# int **var1, # SP+12
|
||||
# int **var2); # SP+16
|
||||
|
||||
# We return our OSErr in d0, and leave the 16 bytes of arguments on the stack for the caller to clean
|
||||
|
||||
image = assemble(f'''
|
||||
myUnitNum equ 52
|
||||
myDRefNum equ ~myUnitNum
|
||||
|
||||
|
||||
cmp.l #1,4(SP)
|
||||
beq getBootBlocks
|
||||
cmp.l #2,4(SP)
|
||||
beq getSysVol
|
||||
cmp.l #3,4(SP)
|
||||
beq mountSysVol
|
||||
|
||||
move.l #-1,d0
|
||||
rts
|
||||
|
||||
|
||||
getBootBlocks
|
||||
lea DiskImage,A0
|
||||
move.l 8(SP),A1 ; Inside the global struct...
|
||||
add.l #$BA,A1 ; ...is an element for the structured part of the boot blocks
|
||||
move.l #138,D0 ; ...of this length
|
||||
|
||||
dc.w $A02E ; BlockMove
|
||||
|
||||
bra return
|
||||
|
||||
|
||||
getSysVol
|
||||
; Our register conventions:
|
||||
; A2 = fake dqe; A3 = dce; A4 = copied driver
|
||||
|
||||
link A6,#-$32
|
||||
movem.l A2-A4/D3,-(SP)
|
||||
|
||||
; Now I copy all this stuff under BufPtr (because this current location will disappear)
|
||||
sub.l #(BufPtrCopyEnd-BufPtrCopy),$10C ; BufPtr
|
||||
move.l $10C,A4 ; ... into A4
|
||||
|
||||
; Copy the driver and image as one chunk
|
||||
lea BufPtrCopy,A0
|
||||
move.l A4,A1
|
||||
move.l #(BufPtrCopyEnd-BufPtrCopy),D0
|
||||
dc.w $A02E ; BlockMove
|
||||
|
||||
; Install the driver in the unit table
|
||||
move.l #myDRefNum,D0
|
||||
dc.w $A43D ; _DrvrInstall ReserveMem
|
||||
bne error
|
||||
|
||||
; Get DCE handle of installed driver
|
||||
move.l $11C,A0 ; UTableBase
|
||||
add.l #myUnitNum*4,A0
|
||||
move.l (A0),A3
|
||||
|
||||
; Lock it down
|
||||
move.l A3,A0
|
||||
dc.w $A029 ; _HLock
|
||||
|
||||
; Populate the empty DCE that DrvrInstall left us
|
||||
move.l (A3),A0 ; A0 = dce ptr
|
||||
|
||||
move.l A4,0(A0) ; dCtlDriver is pointer (not hdl)
|
||||
|
||||
move.w 0(A4),D0 ; drvrFlags
|
||||
and.w #~$0040,D0 ; Clear dRAMBased bit (to treat dCtlDriver as a pointer)
|
||||
move.w D0,4(A0) ; dCtlFlags
|
||||
|
||||
; Copy these other values that apparently the Device Mgr forgets
|
||||
move.w 2(A4),$22(A0) ; drvrDelay to dCtlDelay
|
||||
move.w 4(A4),$24(A0) ; drvrEMask to dCtlEMask
|
||||
move.w 6(A4),$26(A0) ; drvrMenu to dCtlMenu
|
||||
|
||||
; Open the driver
|
||||
lea -$32(A6),A0
|
||||
bsr clearblock
|
||||
lea DrvrNameString,A1
|
||||
move.l A1,$12(A0) ; IOFileName
|
||||
dc.w $A000 ; _Open
|
||||
bne error
|
||||
|
||||
; Attempt to read from the driver
|
||||
; lea -$32(A6),A0
|
||||
; bsr clearblock
|
||||
; move.w #myDRefNum,$18(A0) ; ioRefNum
|
||||
; move.l #$200,$24(A0) ; ioByteCount
|
||||
; dc.w $A002
|
||||
; bne error
|
||||
|
||||
; Create a DQE
|
||||
move.l #$16,D0
|
||||
dc.w $A71E ; _NewPtr ,Sys,Clear
|
||||
add.l #4,A0 ; has some cheeky flags at negative offset
|
||||
move.l A0,A2
|
||||
|
||||
; Point our caller to the fake dqe
|
||||
move.l 4+12(A6),A1
|
||||
move.l A2,(A1)
|
||||
|
||||
; Find a free drive number (nicked this code from BootUtils.a:AddMyDrive)
|
||||
LEA $308,A0 ; [DrvQHdr]
|
||||
MOVEQ #4,D3 ; start with drive number 4
|
||||
CheckDrvNum
|
||||
MOVE.L 2(A0),A1 ; [qHead] start with first drive
|
||||
CheckDrv
|
||||
CMP.W 6(A1),D3 ; [dqDrive] does this drive already have our number?
|
||||
BEQ.S NextDrvNum ; yep, bump the number and try again.
|
||||
CMP.L 6(A0),A1 ; [qTail] no, are we at the end of the queue?
|
||||
BEQ.S GotDrvNum ; if yes, our number's unique! Go use it.
|
||||
MOVE.L 0(A1),A1 ; [qLink] point to next queue element
|
||||
BRA.S CheckDrv ; go check it.
|
||||
|
||||
NextDrvNum
|
||||
; this drive number is taken, pick another
|
||||
|
||||
ADDQ.W #1,D3 ; bump to next possible drive number
|
||||
BRA.S CheckDrvNum ; try the new number
|
||||
GotDrvNum
|
||||
|
||||
; Populate the DQE
|
||||
move.l #$80080000,-4(A2) ; secret flags, see http://mirror.informatimago.com/next/developer.apple.com/documentation/mac/Files/Files-112.html
|
||||
move.w #1,4(A2) ; qType
|
||||
move.w #(DiskImageEnd-DiskImage)&$FFFF,$C(A2) ; dQDrvSz
|
||||
move.w #(DiskImageEnd-DiskImage)>>16,$E(A2) ; dQDrvSz2
|
||||
move.w #0,$A(A2) ; dQFSID should be for a native fs
|
||||
|
||||
; Into the drive queue (which will further populate the DQE)
|
||||
move.l A2,A0 ; A0 = DQE ptr
|
||||
move.w D3,D0
|
||||
swap.w D0 ; D0.H = drive number
|
||||
move.w #myDRefNum,D0 ; D0.L = driver refnum
|
||||
dc.w $A04E ; _AddDrive
|
||||
bne error
|
||||
|
||||
; Save this most precious knowledge for later
|
||||
lea gDriveNum,A0
|
||||
move.w D3,(A0)
|
||||
|
||||
; Clean up our stack frame
|
||||
movem.l (SP)+,A2-A4/D3
|
||||
unlk A6
|
||||
|
||||
bra return
|
||||
|
||||
mountSysVol
|
||||
link A6,#-$32
|
||||
movem.l A2-A4/D3,-(SP)
|
||||
|
||||
lea gDriveNum,A0
|
||||
move.w (A0),D3
|
||||
|
||||
; Make our call able to work! (Fuck, this is ugly)
|
||||
lea NaughtyFSQHSave,A0
|
||||
move.l $3E2,(A0)
|
||||
lea NaughtyFSQHKiller,A0
|
||||
move.l A0,$3E2 ; Disable FSQueueSync
|
||||
|
||||
; Try to mount the volume!
|
||||
lea -$32(A6),A0
|
||||
bsr clearblock
|
||||
|
||||
; FSQueue ourself!
|
||||
; move.w #$A00F,D1
|
||||
; move.l $2AE,A1 ; ROMBase
|
||||
; add.l #$42E8,A1
|
||||
; jsr (A1)
|
||||
|
||||
; MountVol and pray
|
||||
move.w #7,$16(A0) ; ioVRefNum = ioDrvNum = the drive number
|
||||
dc.w $A00F ; _MountVol
|
||||
|
||||
; Tell Elliot that we made it through
|
||||
move.w #$1234,D0
|
||||
dc.w $a9c9
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
movem.l (SP)+,A2-A4/D3
|
||||
unlk A6
|
||||
|
||||
bra return
|
||||
|
||||
return
|
||||
move.l #0,d0
|
||||
rts
|
||||
|
||||
error
|
||||
move.w #$DDDD,D0
|
||||
dc.w $A9C9
|
||||
|
||||
|
||||
clearblock
|
||||
clr.w $0(A0)
|
||||
clr.w $2(A0)
|
||||
clr.w $4(A0)
|
||||
clr.w $6(A0)
|
||||
clr.w $8(A0)
|
||||
clr.w $a(A0)
|
||||
clr.w $c(A0)
|
||||
clr.w $e(A0)
|
||||
clr.w $10(A0)
|
||||
clr.w $12(A0)
|
||||
clr.w $14(A0)
|
||||
clr.w $16(A0)
|
||||
clr.w $18(A0)
|
||||
clr.w $1a(A0)
|
||||
clr.w $1c(A0)
|
||||
clr.w $1e(A0)
|
||||
clr.w $20(A0)
|
||||
clr.w $22(A0)
|
||||
clr.w $24(A0)
|
||||
clr.w $26(A0)
|
||||
clr.w $28(A0)
|
||||
clr.w $2a(A0)
|
||||
clr.w $2c(A0)
|
||||
clr.w $2e(A0)
|
||||
clr.w $30(A0)
|
||||
rts
|
||||
|
||||
|
||||
NaughtyFSQHSave
|
||||
dc.l 0
|
||||
|
||||
|
||||
NaughtyFSQHKiller
|
||||
move.l A0,-(SP)
|
||||
lea NaughtyFSQHSave,A0
|
||||
move.l (A0),$3E2
|
||||
move.l (SP)+,A0
|
||||
|
||||
add.l #4,SP
|
||||
rts
|
||||
|
||||
|
||||
DrvrNameString
|
||||
dc.b 11, ".netRamDisk"
|
||||
|
||||
|
||||
gDriveNum
|
||||
dc.w 0
|
||||
|
||||
|
||||
; code on this side is for start only, and stays in the netBOOT driver globals until released
|
||||
|
||||
BufPtrCopy
|
||||
|
||||
; code on this side gets copied beneath BufPtr (is that the best place??)
|
||||
|
||||
|
||||
; Shall we start with a driver?
|
||||
DrvrBase
|
||||
dc.w $4F00 ; dReadEnable dWritEnable dCtlEnable dStatEnable dNeedLock
|
||||
dc.w 0 ; delay
|
||||
dc.w 0 ; evt mask
|
||||
dc.w 0 ; menu
|
||||
|
||||
dc.w DrvrOpen-DrvrBase
|
||||
dc.w DrvrPrime-DrvrBase
|
||||
dc.w DrvrControl-DrvrBase
|
||||
dc.w DrvrStatus-DrvrBase
|
||||
dc.w DrvrClose-DrvrBase
|
||||
dc.b 11, ".netRamDisk"
|
||||
|
||||
; a0=iopb, a1=dce on entry to all of these...
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
DrvrOpen
|
||||
move.w #0,$10(A0) ; ioResult
|
||||
rts
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
DrvrPrime
|
||||
movem.l A0-A4/D0-D4,-(SP)
|
||||
move.l A0,A2 ; ParamBlk
|
||||
move.l A1,A3 ; DCE
|
||||
|
||||
cmp.b #2,$7(A2) ; ioTrap == aRdCmd
|
||||
bne.s notRead
|
||||
|
||||
; D0 = number of bytes to read
|
||||
move.l $24(A2),D0 ; ioReqCount
|
||||
move.l D0,$28(A2) ; -> ioActCount
|
||||
|
||||
; D1 = source offset
|
||||
clr.l D1
|
||||
cmp.b #1,$2C(A2) ; ? ioPosMode == fsFromStart
|
||||
beq.s dontAddMark
|
||||
add.l $10(A3),D1 ; add dCtlPosition
|
||||
dontAddMark
|
||||
cmp.b #3,$2C(A2) ; ? ioPosMode == fsFromMark
|
||||
bne.s dontAddOffset
|
||||
add.l $2E(A2),D1 ; add ioPosOffset
|
||||
dontAddOffset
|
||||
|
||||
; Advance the pointer
|
||||
move.l D0,D2
|
||||
add.l D1,D2 ; calculate new position
|
||||
move.l D2,$10(A3) ; -> dCtlPosition
|
||||
move.l D2,$2E(A2) ; -> ioPosOffset
|
||||
|
||||
; Do the dirty
|
||||
lea DiskImage,A0
|
||||
add.l D1,A0
|
||||
move.l $20(A2),A1 ; ioBuffer
|
||||
dc.w $A02E ; BlockMove
|
||||
|
||||
move.w #0,$10(A2) ; ioResult
|
||||
bra primeFinish
|
||||
|
||||
|
||||
notRead
|
||||
move.w #$2222,D0
|
||||
dc.w $A9C9
|
||||
cmp.b #3,7(A0) ; ioTrap == aRdCmd
|
||||
|
||||
|
||||
primeFinish
|
||||
movem.l (SP)+,A0-A4/D0-D4
|
||||
bra DrvrFinish
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
DrvrControl
|
||||
move.w #$2222,D0
|
||||
dc.w $A9C9
|
||||
|
||||
move.w #0,$10(A0) ; ioResult
|
||||
bra DrvrFinish
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
DrvrStatus
|
||||
cmp.w #6,$1A(A0)
|
||||
beq.s status_fmtLstCode
|
||||
cmp.w #8,$1A(A0)
|
||||
beq.s status_drvStsCode
|
||||
bra.s status_unknown
|
||||
|
||||
status_fmtLstCode ; tell them about our size
|
||||
move.l A2,-(SP)
|
||||
move.w #1,$1C(A0)
|
||||
move.w $1C+4(A0),A2
|
||||
move.l #(DiskImageEnd-DiskImage),0(A2)
|
||||
move.l #$40000000,4(A2)
|
||||
move.l (SP)+,A2
|
||||
|
||||
move.w #0,$10(A0) ; ioResult = statusErr
|
||||
bra DrvrFinish
|
||||
|
||||
status_drvStsCode ; tell them about some of our flags
|
||||
move.w #0,$1C(A0) ; csParam[0..1] = track no (0)
|
||||
move.l #80080000,$1C+2(A0) ; csParam[2..5] = same flags as dqe
|
||||
|
||||
move.w #0,$10(A0) ; ioResult = noErr
|
||||
bra DrvrFinish
|
||||
|
||||
status_unknown
|
||||
move.w $1A(A0),D0 ; dodgy, for debugging
|
||||
add.w #$3000,D0
|
||||
dc.w $A9C9
|
||||
|
||||
move.w #-18,$10(A0) ; ioResult
|
||||
bra DrvrFinish
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
DrvrClose
|
||||
move.w #$4444,D0
|
||||
dc.w $A9C9
|
||||
|
||||
move.w #0,$10(A0) ; ioResult
|
||||
rts
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
DrvrFinish
|
||||
move.w 6(A0),D1 ; iopb.ioTrap
|
||||
btst #9,D1 ; noQueueBit
|
||||
bne.s DrvrNoIoDone
|
||||
move.l $8FC,-(SP) ; jIODone
|
||||
DrvrNoIoDone
|
||||
rts
|
||||
|
||||
|
||||
DiskImage {chr(10).join(' dc.b ' + str(x) for x in disk_image)}
|
||||
DiskImageEnd
|
||||
|
||||
BufPtrCopyEnd
|
||||
''')
|
||||
|
||||
|
||||
|
||||
|
||||
image = image.ljust(512*20)
|
||||
|
||||
|
||||
ANY = "0.0.0.0"
|
||||
MCAST_ADDR = "239.192.76.84"
|
||||
MCAST_PORT = 1954
|
||||
|
||||
|
@ -25,15 +481,223 @@ status = sock.setsockopt(socket.IPPROTO_IP,
|
|||
socket.IP_ADD_MEMBERSHIP,
|
||||
socket.inet_aton(MCAST_ADDR) + socket.inet_aton(ANY))
|
||||
|
||||
# setblocking(0) is equiv to settimeout(0.0) which means we poll the socket.
|
||||
# But this will raise an error if recv() or send() can't immediately find or send data.
|
||||
# sock.setblocking(0)
|
||||
|
||||
|
||||
# Wrap up in all sorts of crap...
|
||||
def mk_ddp(dest_node, dest_socket, src_node, src_socket, proto_type, data):
|
||||
# Wrap into DDP datagram
|
||||
data = struct.pack('>HBBB', len(data) + 5, dest_socket, src_socket, proto_type) + data
|
||||
|
||||
# Wrap into LLAP packet
|
||||
data = struct.pack('>BBB', dest_node, src_node, 1) + data
|
||||
|
||||
# Wrap the novel LToUDP header
|
||||
data = my_unique_ltoudp_id + data
|
||||
|
||||
return data
|
||||
|
||||
|
||||
|
||||
def pstring(x):
|
||||
try:
|
||||
x = x.encode('mac_roman')
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
return bytes([len(x)]) + x
|
||||
|
||||
|
||||
|
||||
while 1:
|
||||
try:
|
||||
data, addr = sock.recvfrom(1024)
|
||||
except socket.error as e:
|
||||
pass
|
||||
data, addr = sock.recvfrom(1024)
|
||||
|
||||
# Okay... LToUDP parsing here. Let's start with the LLAP packet.
|
||||
# Man, chaining protocols is hard. This will inevitably require a rewrite.
|
||||
# Be careful to keep this as one cascading thing...
|
||||
|
||||
if len(data) < 4: continue
|
||||
if data.startswith(my_unique_ltoudp_id): continue
|
||||
data = data[4:] # Trim down to LLAP packet (not "frame")
|
||||
|
||||
if len(data) < 3: continue
|
||||
llap_dest_node = data[0]
|
||||
llap_src_node = data[1]
|
||||
llap_proto_type = data[2]
|
||||
data = data[3:] # Trim down to LLAP payload
|
||||
|
||||
# Try to extract a DDP header, which is all we want!
|
||||
if llap_proto_type == 1:
|
||||
# ddp, short
|
||||
if len(data) < 5: continue
|
||||
ddp_len, ddp_dest_socket, ddp_src_socket, ddp_proto_type = struct.unpack_from('>HBBB', data)
|
||||
data = data[5:ddp_len]
|
||||
elif llap_proto_type == 2:
|
||||
# ddp, long (what should we do with this extra information?)
|
||||
if len(data) < 13: continue
|
||||
ddp_len, ddp_cksum, ddp_dest_net, ddp_src_net, ddp_dest_node, ddp_src_node, ddp_dest_socket, ddp_src_socket = struct.unpack_from('>4H5B', data)
|
||||
ddp_hop_count = (ddp_len >> 10) & 0xF
|
||||
ddp_len &= 0x3FF
|
||||
data = data[13:ddp_len]
|
||||
else:
|
||||
print("From: ", addr)
|
||||
print("Data: ", data)
|
||||
# llap control packet -- can probably ignore!
|
||||
continue
|
||||
|
||||
print(f'datagram {llap_src_node}:{ddp_src_socket}->{llap_dest_node}:{ddp_dest_socket}', end=' ')
|
||||
|
||||
if ddp_proto_type == 2:
|
||||
# Name Binding Protocol
|
||||
|
||||
if len(data) < 2: continue
|
||||
nbp_func = data[0] >> 4
|
||||
nbp_tuple_cnt = data[0] & 0xF
|
||||
nbp_id = data[1]
|
||||
data = data[2:]
|
||||
|
||||
nbp_tuples = []
|
||||
while data and len(nbp_tuples) < nbp_tuple_cnt:
|
||||
if len(data) < 5: break
|
||||
this_tuple = list(struct.unpack_from('>HBBB', data))
|
||||
data = data[5:]
|
||||
for i in range(3):
|
||||
# This should be coded more defensively, perhaps using exceptions
|
||||
this_tuple.append(data[1:1+data[0]].decode('mac_roman'))
|
||||
data = data[1+data[0]:]
|
||||
nbp_tuples.append(tuple(this_tuple))
|
||||
|
||||
|
||||
print('nbp func', nbp_func, 'id', nbp_id)
|
||||
for t in nbp_tuples:
|
||||
print(f' net:node:sock={t[0]}:{t[1]}:{t[2]} enum={t[3]} object:type@zone={t[4]}:{t[5]}@{t[6]}')
|
||||
|
||||
if nbp_func == 2:
|
||||
# NBP LkUp
|
||||
|
||||
if len(nbp_tuples) == 1 and nbp_tuples[0][5] == 'BootServer': # Looking for us
|
||||
sock.sendto(mk_ddp(
|
||||
dest_node=llap_src_node, dest_socket=ddp_src_socket,
|
||||
src_node=99, src_socket=99,
|
||||
proto_type=2,
|
||||
data=bytes([0x31, nbp_id]) + # 3=LkUp-Reply, 1=number-of-tuples
|
||||
struct.pack('>HBBB', # Pack the first tuple
|
||||
0, # (My) Network number
|
||||
99, # (My) Node ID
|
||||
99, # (My) Socket number
|
||||
0, # (My) Enumerator (i.e. which of many possible names for 99/99 is this?)
|
||||
) +
|
||||
pstring(nbp_tuples[0][5]) + pstring('BootServer') + pstring('*')
|
||||
),
|
||||
(MCAST_ADDR, MCAST_PORT))
|
||||
|
||||
elif ddp_proto_type == 10:
|
||||
# Mysterious ATBOOT protocol
|
||||
|
||||
if len(data) < 2: continue
|
||||
boot_type, boot_vers = struct.unpack_from('>BB', data)
|
||||
data = data[2:]
|
||||
|
||||
# rbNullCommand EQU 0 ; ignore this one
|
||||
# rbMapUser EQU 1 ; user record request
|
||||
# rbUserReply EQU 2 ; user record reply
|
||||
# rbImageRequest EQU 3 ; image request & bitmap
|
||||
# rbImageData EQU 4 ; image data
|
||||
# rbImageDone EQU 5 ; server done with current image
|
||||
# rbUserRecordUpdate EQU 6 ; new user info to server
|
||||
# rbUserRecordAck EQU 7 ; info received from server
|
||||
|
||||
if boot_type == 1:
|
||||
# It might be neater to trim off the "type" and "version" fields a bit earlier
|
||||
boot_machine_id, boot_timestamp, boot_username = struct.unpack_from('>HL 34p', data)
|
||||
|
||||
print(f'atboot type={boot_type}, vers={boot_vers}, machineID={boot_machine_id}, userName={boot_username}')
|
||||
|
||||
# // This defines a user record.
|
||||
# // Some of these fields can be determined on the fly by the boot server,
|
||||
# // while others are stored on disk by the boot server
|
||||
# typedef struct
|
||||
# {
|
||||
# char serverName[serverNameLength]; // server name to boot off of
|
||||
# char serverZone[zoneNameLength]; // and the zone it lives in
|
||||
# char serverVol[volNameLength]; // the volume name
|
||||
# short serverAuthMeth; // authentication method to use (none, clear txt, rand)
|
||||
# unsigned long sharedSysDirID; // dir id of shared system folder
|
||||
# unsigned long userDirID; // dir id of the user's private system folder
|
||||
# unsigned long finderInfo[8]; // blessed folder dir id, startup folder dir id etc...
|
||||
# unsigned char bootBlocks[138]; // see Inside Mac V-135
|
||||
# unsigned short bootFlag; // server based flags
|
||||
# unsigned char pad[306-18]; // <pd 5>pad to ddpMaxData
|
||||
# }userRecord;
|
||||
|
||||
# // This defines the user record reply sent to workstations.
|
||||
# typedef struct
|
||||
# {
|
||||
# unsigned char Command; /* record or image request command word */
|
||||
# unsigned char pversion; /* version of boot protocol spoken by requestor */
|
||||
# unsigned short osID; /* type and version of os */
|
||||
# unsigned int userData; /* time stamp goes here */
|
||||
# unsigned short blockSize; /* size of blocks we will send */
|
||||
# unsigned short imageID; /* image ID */
|
||||
# short result; /* error codes */
|
||||
# unsigned int imageSize; /* size of image in blocks */
|
||||
# userRecord userRec; /* tell user where to go */
|
||||
# }BootPktRply;
|
||||
|
||||
# Ignore the silly user record. Just make the fucker work!
|
||||
sock.sendto(mk_ddp(
|
||||
dest_node=llap_src_node, dest_socket=ddp_src_socket,
|
||||
src_node=99, src_socket=99,
|
||||
proto_type=10,
|
||||
data=struct.pack('>BBHLHHhL', 2, 1, boot_machine_id, boot_timestamp, 512, 0, 0, len(image) // 512).ljust(586, b'\0')
|
||||
),
|
||||
(MCAST_ADDR, MCAST_PORT))
|
||||
|
||||
elif boot_type == 3:
|
||||
# It seems to want part of the boot image!
|
||||
# print('it wants data', data)
|
||||
|
||||
# typedef struct
|
||||
# {
|
||||
# unsigned short imageID; /* */
|
||||
# unsigned char section; /* "section" of the image the bitmap refers to */
|
||||
# unsigned char flags; /* ??? */
|
||||
# unsigned short replyDelay;
|
||||
# unsigned char bitmap[512]; /* bitmap of the section of the image requested */
|
||||
# }bir; // Boot Image Req
|
||||
|
||||
print('Boot Image Req')
|
||||
|
||||
if len(data) < 6: continue
|
||||
boot_image_id, boot_section, boot_flags, boot_reply_delay = struct.unpack_from('>HBBH', data)
|
||||
data = data[6:]
|
||||
|
||||
# Okay, pretty much just send the bits that were requested!
|
||||
print('Sending blocks')
|
||||
for blocknum in range(len(image) // 512):
|
||||
if len(data) > blocknum // 8 and (data[blocknum // 8] >> (blocknum % 8)) & 1:
|
||||
# typedef struct {
|
||||
# unsigned char packetType; /* The command number */
|
||||
# unsigned char packetVersion; /* Protocol version number */
|
||||
# unsigned short packetImage; /* The image this block belongs to */
|
||||
# unsigned short packetBlockNo; /* The block of the image (starts with 1) */
|
||||
# unsigned char packetData[512];/* the data portion of the packet */
|
||||
# } BootBlock,*BootBlockPtr;
|
||||
|
||||
# print(f' {blocknum}', end='', flush=True)
|
||||
sock.sendto(mk_ddp(
|
||||
dest_node=llap_src_node, dest_socket=ddp_src_socket,
|
||||
src_node=99, src_socket=99,
|
||||
proto_type=10,
|
||||
data=struct.pack('>BBHH', 4, 1, boot_image_id, blocknum) + image[blocknum*512:blocknum*512+512]
|
||||
),
|
||||
(MCAST_ADDR, MCAST_PORT))
|
||||
|
||||
# time.sleep(0.5)
|
||||
# break # wait for another request, you mofo!
|
||||
|
||||
elif boot_type > 4:
|
||||
print(f'protocol10 type={boot_type} vers={boot_vers} contents={repr(data)}')
|
||||
|
||||
|
||||
|
||||
|
||||
else:
|
||||
print('unknown ddp payload', ddp_proto_type)
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
#!/bin/bash
|
||||
|
||||
trap "trap - SIGTERM && kill -- -$$" SIGINT SIGTERM EXIT
|
||||
|
||||
./NetBoot.py & cp xo.rom edit.rom && ./xo-rom-enable-netboot.py edit.rom && Mini\ vMac\ Classic.app/Contents/MacOS/minivmac edit.rom && kill %1
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -34,7 +34,7 @@ with open(sys.argv[1], 'r+b') as f:
|
|||
garbage_location = 0x67046 # The MacsBug copyright string in the ROM Disk
|
||||
|
||||
# Make out own structure, as adapted from NetBoot.h
|
||||
the_netboot_structure = struct.pack('>BBBB BB 8s 32s 8s H',
|
||||
the_netboot_structure = struct.pack('>BBBB BB 16s 32s 8s H', # Note that "int"s are 4 bytes, somehow!
|
||||
# First 4 bytes at PRAM+4
|
||||
1, # char osType; /* preferred os to boot from */
|
||||
1, # char protocol; /* preferred protocol to boot from */
|
||||
|
@ -44,12 +44,16 @@ with open(sys.argv[1], 'r+b') as f:
|
|||
# Now, the AppleTalk-protocol-specific part
|
||||
0, # unsigned char nbpVars; /* address of last server that we booted off of */
|
||||
0, # unsigned char timeout; /* seconds to wait for bootserver response */
|
||||
b'', # unsigned int signature[4]; /* image signature */
|
||||
b'', # unsigned int signature[4]; /* image signature */ Elliot notes: zeroes match anything!
|
||||
b'Elliot', # char userName[31]; /* an array of char, no length byte */
|
||||
b'volapuk', # char password[8]; /* '' */
|
||||
0x7777, # short serverNum; /* the server number */
|
||||
0xBABE, # short serverNum; /* the server number */
|
||||
).ljust(72, b'\0')
|
||||
|
||||
# the_netboot_structure = bytearray(the_netboot_structure)
|
||||
# for i, j in enumerate(range(54, len(the_netboot_structure))):
|
||||
# the_netboot_structure[j] = i
|
||||
|
||||
# This is the guts of the NetBoot ReadPRAM procedure
|
||||
f.seek(0x1DF08)
|
||||
write_asm(f, f'''
|
||||
|
@ -64,6 +68,17 @@ with open(sys.argv[1], 'r+b') as f:
|
|||
f.seek(garbage_location)
|
||||
f.write(the_netboot_structure)
|
||||
|
||||
# Do the dodgy... cancel signature validation!
|
||||
f.seek(0x21A84)
|
||||
while f.tell() < 0x21A98:
|
||||
f.write(b'Nq') # nop
|
||||
|
||||
# Work around Mini vMac's cutesy many-drives hack (it steals out preferred drivenum of 4 from us)
|
||||
f.seek(0x1DF51) # AddMyDrive: moveq #4,d9
|
||||
f.write(b'\x10')
|
||||
f.seek(0x1DFDF) # myExtFSFilter: cmp.w #4,d0
|
||||
f.write(b'\x10')
|
||||
|
||||
# Enable this to make a SysError, for rudimentaly debug output
|
||||
# f.seek(0x1DD4E)
|
||||
# f.write(b'\xA9\xC9')
|
||||
f.seek(0x878C)
|
||||
f.write(assemble('move.w #0xFFFF,D0 \n .2byte 0xA9C9'))
|
||||
|
|
Loading…
Reference in New Issue