The Apple Network Server's all-too-secret weapon (featuring PPC Toolbox)

The Apple Network Server's all-too-secret weapon (featuring PPC Toolbox)

An incredible history of the Apple Network Server (running AIX on PowerPC), along with network IPC via AppleTalk to a 68k.

oldvcr.blogspot.com
u/Ill-Quit6803 — 10 days ago
▲ 12 r/PowerPC

String copy Algorithm for the 601 + Qemu Bug

The lscbx instruction is almost a perfect string copy instruction, XER takes a number of bytes to load up regs with at max, and a sentinel byte to stop at. This instruction appears to only be present on the 601. I wrote the following string copy algorithm for the 32 bit Linux ABI on the 601 to play around with it.

##
# char * my_strcpy(char *dest, char *src);
##
.globl my_strcpy
my_strcpy:
mflr 0
stw 0, 4(1)
# Preconditions Check:
# dest, and src are non-null
cmpwi 3, 0
beqlr
cmpwi 4, 0
beqlr
stw 3, 8(1)# Save Dest pointer in Senders output argument area
# Enter Stack Frame
# | LR SAVE |
# | backchain | 128
# |-----------|
# | r31 | 124
# | ... | ...
# | r14 | 56
# | padding ..|
# | ... |
# | Param save| 28
# | LR Save | 4
# | backchain | 0
# LR/Paramter Save Area reserved reserved at bottom 6 words
# 18 words reserved at top for R14:R31 inclusive (24*4=96, quadword align to 128)
stwu 1, -128(1)
stmw 14, 56(1)
# Setup for String Copy, R5:R31 108 bytes at a time
# stopping at NUL character
li 5, 0x6c # this will stay 0x6c as long as loop runs with no match
mtxer 5
LT..0:
lscbx. 5, 0, 4 # 108 bytes or EOF
stswx 5, 0, 3 # 108 byes or til EOF
# Increment Read/Write pointers
addi 3, 3, 0x6c # we may over shoot on the tail, but unless match
addi 4, 4, 0x6c # occured, it doesn't matter
# Check termination condition
bne LT..0
# done Restore
lmw 14,56(1)
addi 1, 1, 128
# Set Return Pointer
lwz 3, 8(1)
lwz 0, 4(1)
mtlr 0
blr

This algorithm is quite possibly very slow. The stmw/lmw instructions arn't necessarily favored, shorts string won't be worth spilling all non-volatile registers to memory over. lscbx may be very slow. This isn't intended to be used.

Qemu post 6.2 actually dropped support for the 601, the only CPU that supports the intruction. Upon rebuilding qemu, observed that the lscbx instruction has the following generator from targets/ppc/translate.c around line 5700.

/* lscbx - lscbx. */
static void gen_lscbx(DisasContext *ctx)
{
    TCGv t0 = tcg_temp_new();
    TCGv_i32 t1 = tcg_const_i32(rD(ctx->opcode));
    TCGv_i32 t2 = tcg_const_i32(rA(ctx->opcode));
    TCGv_i32 t3 = tcg_const_i32(rB(ctx->opcode));

    gen_addr_reg_index(ctx, t0);
    gen_helper_lscbx(t0, cpu_env, t0, t1, t2, t3);
    tcg_temp_free_i32(t1);
    tcg_temp_free_i32(t2);
    tcg_temp_free_i32(t3);
    tcg_gen_andi_tl(cpu_xer, cpu_xer, ~0x7F);
    tcg_gen_or_tl(cpu_xer, cpu_xer, t0);
    if (unlikely(Rc(ctx->opcode) != 0)) {
        gen_set_Rc0(ctx, t0);
    }
    tcg_temp_free(t0);
}

Notably, you can see that if Rc bit is set (IE. set flags), then CR0 is set by comparing the returned value of the instruction in t0 ( then number of bytes copied) with zero.

The AIX Assembly reference has the following description of the behavior:

If Rc = 1 and XER(25-31) = 0, then Condition Register Field 0 is

undefined. If Rc = 1 and XER(25-31) <> 0, then Condition Register Field 0 is set as follows:

LT, GT, EQ, SO = b'00'||match||XER(SO)

This is the behavior that DingusPCC implements in cpu/ppc/poweropcodes.cpp

void dppc_interpreter::power_lscbx(uint32_t opcode) {

if (rec) {

ppc_state.cr =

(ppc_state.cr & 0x0FFFFFFFUL) |

(is_match ? CRx_bit::CR_EQ : 0) |

((ppc_state.spr[SPR::XER] & XER::SO) >> 3);

}}

Qemu actually removed the code for the 601 anyway. But, I found this an intresting disgression. lscbx is pretty cool, its seems to be whats missing from the x86 string instructions, which can't copy until either a NUL OR a limit like the 601 could.

Why did qemu deprecate the 601? What emulators do you guys use?

reddit.com
u/Ill-Quit6803 — 11 days ago
▲ 9 r/aix

Hello World to AIX (Power Assembly)

I have been exploring AIX and I thought I would write up how to write a simple Hello World program in assembly using the AIX toolchain and debuggers.

Some background about the toolchain : AIX has an `as` assembler as apart of the base image, and an `ld` linker, these are not the GNU as/ld , and have different options. Additionally, The outputs are XCOFF binary format, not ELFs. The AIX debugger is ` dbx`. All of these have man pages where some basic usage is called out.

AIX runs on POWER architecture machines, which are a RISC machine. The property of importance here is that the instructions are fixed width (4 bytes), and the word size/address space is 8 bytes wide. Obviously, you can't fit an entire address in an instruction, and so AIX (and Linux PPC), use complicated relocation and register relative addressing to enable loads and stores. This is enabled by the TOC, which is customarily place in register `r2` . Each function can be associated with its own TOC (or a shared module TOC), and function descriptors (DS) are used to associate a function with a TOC.

Behold, the assembly (which you can easily generate simiiar examples with gcc as apart of the AIXToolbox).

.extern .kwrite #external symbols for syscalls defined

.extern ._exit

.set STDOUT_FILENO,1

.globl __start[DS]

.csect __start[DS],3 # function descriptor for __start entry point

__start:

.llong .__start, TOC[tc0], 0

.csect .text[PR],5

.globl .__start

.__start: # actual code for entrypoint

mflr 0 #prologue, move LR to register r0

std 0, 16(1) #save r0 (IE. the LR) in sender link area

stdu 1, -128(1) #allocate our stack frame (Required by ABI) because we are non-leaf

li 3, STDOUT_FILENO # r3 is used as argument1 for syscall

ld 4, hello_world_desc(2) # load address of string into r4 relative to r2 (TOC)

li 5, hello_world_len # r5 (argument 3) set to length

bl .kwrite # do syscall via call (see notes below)

nop # ABI required by for linker to fixup r2

xor 3, 3, 3 # zero exit code stored in argument r3 for exit syscall

bl ._exit # invoke exit syscall

nop

addi 1, 1, 128 # dead code for epilogue

ld 0, 16(1)

mtlr 0

blr # this would otherwise be a ret instruction

.csect .data[RW], 4

hello_world_str:

.byte "Hello, World"

.byte 10

.set hello_world_len, $ - hello_world_str

.toc

hello_world_desc: .tc hello_world_str[TC],hello_world_str

A Note on Syscalls on AIX:

AIX uses a convention somewhat analogous to Windows NT, in that applications ordinarily do not make syscalls directly, and the syscall number isn't an ABI feature (a la Linux). Windows NT exposes its sycall layer through ntdll.dll as functions. This shared library is mapped in all processes loaded, requested or not. Similarly, AIX maps the kernel (/unix) unasked, at address 0x0000000000000000 . The kernel exposes a set of symbols to processes that the loader is capable of binding in processes. The kernel text actually contains the functions which wrap syscalls (a la ntdll.dll), and userspace programs call these entrypoints which preform a syscall instruction to cross the protection boundary.

This raises a small difficulty linking, these symbols must be bound by linking against the kernel import file. This is contained as apart of the the `bos.adt` packages. Alternatively, what I did was create my own import file (./kern.imp)

#!/unix

.kwrite

._exit

This can be assembled as follows:

`as -o hello_world_kernel.o -a64 -mPWR9 hello_world_kernel.S`

Note that the assembler defaults to the 32 bit unless '-a64' is provided.

I used to following command to link

`ld -b64 -o hello_world_kernel hello_world_kernel.o -bI:./kern.imp`

We can now observe the binary. The traditional UNIX equivalent of strace is `truss`.

truss ./hello_world_kernel

execve("./hello_world_kernel", 0x2FF22C54, 0x20016878) argc: 1

kusla(6, 0x09FFFFFFF0001170) = 0

Hello, World

kwrite(1, " H e l l o , W o r l d".., 13) = 13

_exit(0)

Additionally we can trace up to the syscall in dbx.

$ dbx ./hello_world_kernel

Type 'help' for help.

reading symbolic information ...warning: no source compiled with -g

(dbx) stop in __start

[1] stop in __start

(dbx) map

Entry 1:

Object name: hello_world_kernel

Text origin: 0x100000000

Text length: 0x3d3

Data origin: 0x110000290

Data length: 0x40

File descriptor: 0x4

Entry 2:

Object name: /usr/ccs/bin/usla64

Text origin: 0x9fffffff0000000

Text length: 0xf82e

Data origin: 0x9fffffff000f82e

Data length: 0x0

File descriptor: 0x5

(dbx) cont

[1] stopped in __start at 0x100000200

0x100000200 (__start) 7c0802a6 mflr r0

(dbx) listi

0x100000200 (__start) 7c0802a6 mflr r0

0x100000204 (__start+0x4) f8010010 std r0,0x10(r1)

0x100000208 (__start+0x8) f821ff81 stdu r1,-128(r1)

0x10000020c (__start+0xc) 38600001 li r3,0x1

0x100000210 (__start+0x10) e8820000 ld r4,0x0(r2)

0x100000214 (__start+0x14) 38a0000d li r5,0xd

0x100000218 (__start+0x18) 48000025 bl 0x10000023c (kwrite)

0x10000021c (__start+0x1c) e8410028 ld r2,0x28(r1)

0x100000220 (__start+0x20) 7c631a78 xor r3,r3,r3

0x100000224 (__start+0x24) 48000041 bl 0x100000264 (_exit)

(dbx) stepi

stopped in __start at 0x100000204

0x100000204 (__start+0x4) f8010010 std r0,0x10(r1)

...

0x100000218 (__start+0x18) 48000025 bl 0x10000023c (kwrite)

(dbx) stepi

stopped in kwrite at 0x10000023c

0x10000023c (kwrite) e9820008 ld r12,0x8(r2)

(dbx) stepi

stopped in kwrite at 0x100000240

0x100000240 (kwrite+0x4) f8410028 std r2,0x28(r1)

(dbx) stepi

stopped in kwrite at 0x100000244

0x100000244 (kwrite+0x8) e80c0000 ld r0,0x0(r12)

(dbx) stepi

stopped in kwrite at 0x100000248

0x100000248 (kwrite+0xc) e84c0008 ld r2,0x8(r12)

(dbx) stepi

stopped in kwrite at 0x10000024c

0x10000024c (kwrite+0x10) 7c0903a6 mtctr r0

(dbx) stepi

stopped in kwrite at 0x100000250

0x100000250 (kwrite+0x14) 4e800420 bctr

(dbx) p $r0

0x0000000000003700

(dbx) listi 0x3700

0x0000000000003700 4cc63342 crorc cr6,cr6,cr6

0x0000000000003704 44000002 sc 0x0

...

(dbx) nexti

Hello, World

stopped in __start at 0x10000021c

0x10000021c (__start+0x1c) e8410028 ld r2,0x28(r1)

(dbx) stepi

stopped in __start at 0x100000220

0x100000220 (__start+0x20) 7c631a78 xor r3,r3,r3

(dbx) stepi

stopped in __start at 0x100000224

0x100000224 (__start+0x24) 48000041 bl 0x100000264 (_exit)

(dbx) stepi

execution completed

(dbx) q

I am continuing to explore AIX, so some of this analysis may be incorrect , or change later. But, its been fun so far.

Post RISC assembly or other AIX secrets.

reddit.com
u/Ill-Quit6803 — 12 days ago

NeXTSTEP Release 3: A Demonstration with Steve Jobs

Here is an interesting demo with a young Steve Jobs, where he walks over the NeXTSTEP 3 OS.

I am amazed at how polished and integrated the system is, even with the 3rd party products of the time (Novell Netware, AppleTALK), and intra-application. Features like 'services' exist in Android sharing, but generally still don't seem common in our computing environments.

youtube.com
u/Ill-Quit6803 — 14 days ago

Metasploit framework on AIX 7.2

In a strange juxtaposition, here is Metasploit running on modernish AIX (7.2), with CDE, the vintage UNIX desktop. AIX long ago abandoned the workstation market, but even today, you can still install CDE on AIX 7.3.

In a Youtube, video , the author of the blog from which the photos are taken, demos catching a callback metasploit shell from Windows 10.

Running MSF on metasploit was no easy task, and the details of how the author was able to edit the build system of metasploit dependencies to enable building are here. Blog Post with Details

I found this mixture of old and new, and the CDE aesthetic quite compelling.

u/Ill-Quit6803 — 18 days ago