├── .gitignore ├── bin └── .gitignore ├── proof.jpg ├── kpartx.sh ├── boot.tbxi ├── bootinfo.txt ├── hfs.map ├── README.md ├── src ├── ofwmagic.s └── main.c └── Makefile /.gitignore: -------------------------------------------------------------------------------- 1 | dmg_mount/* 2 | -------------------------------------------------------------------------------- /bin/.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.dmg 3 | *.iso 4 | *.img 5 | *.elf 6 | -------------------------------------------------------------------------------- /proof.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rndtrash/ppc-experiments/master/proof.jpg -------------------------------------------------------------------------------- /kpartx.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | sudo mkfs.hfsplus -v Hellorld bin/hellorld.dmg 3 | LOOP=$(sudo kpartx -s -a -v bin/hellorld.dmg | awk -F'[ ]' '{print $3}' | tail -n1 ) 4 | sudo mount -o loop /dev/mapper/$LOOP ./dmg_mount/ 5 | -------------------------------------------------------------------------------- /boot.tbxi: -------------------------------------------------------------------------------- 1 | 2 | Hellorld! 3 | Hellorld 4 | 1 5 | 6 | MacRISC MacRISC3 MacRISC4 7 | 8 | 9 | " screen" output 10 | boot &device;:&partition;,\ppc\boot.elf 11 | 12 | 13 | -------------------------------------------------------------------------------- /bootinfo.txt: -------------------------------------------------------------------------------- 1 | 2 | Hellorld! 3 | Hellorld 4 | 1 5 | 6 | MacRISC MacRISC3 MacRISC4 7 | 8 | 9 | " screen" output 10 | boot &device;:&partition;,\ppc\boot.elf 11 | 12 | 13 | -------------------------------------------------------------------------------- /hfs.map: -------------------------------------------------------------------------------- 1 | # ext. xlate creator type comment 2 | .hqx Ascii 'BnHx' 'TEXT' "BinHex file" 3 | .sit Raw 'SIT!' 'SITD' "StuffIT Expander" 4 | .mov Raw 'TVOD' 'MooV' "QuickTime Movie" 5 | .deb Raw 'Debn' 'bina' "Debian package" 6 | .bin Raw 'ddsk' 'DDim' "Floppy or ramdisk image" 7 | .img Raw 'ddsk' 'DDim' "Floppy or ramdisk image" 8 | .b Raw 'UNIX' 'tbxi' "bootstrap" 9 | .elf Raw 'UNIX' 'boot' "bootstrap" 10 | vmlinux Raw 'UNIX' 'boot' "bootstrap" 11 | .conf Raw 'UNIX' 'conf' "bootstrap" 12 | .tbxi Raw 'UNIX' 'conf' "bootstrap" 13 | * Ascii '????' '????' "Text file" 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Hellorld Open Firmware 2 | 3 | ![An iBook G3 Dual USB booting the Hellorld boot.elf](/proof.jpg) 4 | 5 | Inspired by [thamugadi/powerpc-ofw-boot](https://github.com/thamugadi/powerpc-ofw-boot) 6 | 7 | # Building 8 | 9 | 1. Install kpartx, hfsplus tools with mkfs.hfsplus program and GCC with Binutils for PPC 10 | 2. Run `make` 11 | 12 | # To all the rights owners 13 | 14 | At this point, I'm just kit-bashing random snippets from the Internet, 15 | so please forgive me if I stole your code. I promise to replace it 16 | with the original code as soon as I get this thing working. 17 | 18 | Thank you for your understanding. 19 | 20 | # Links 21 | 22 | - [Mac OS X Internals: A Systems Approach, Section 4.10. BootX](https://flylib.com/books/en/3.126.1.47/1/) 23 | - [The BootX source code](https://github.com/apple-oss-distributions/BootX/) 24 | - [OpenBIOS source code](https://github.com/openbios/openbios/) 25 | - [NetBSD ofwboot for MacPPC source code](https://github.com/NetBSD/src/blob/master/sys/arch/macppc/stand/ofwboot/Makefile) 26 | -------------------------------------------------------------------------------- /src/ofwmagic.s: -------------------------------------------------------------------------------- 1 | .section ".note","",@note 2 | 3 | # note header 4 | 5 | # length of name 6 | .long 8 7 | 8 | # note descriptor size 9 | .long 24 10 | 11 | # note type (IEEE 1275) 12 | .long 0x1275 13 | 14 | # name of owner 15 | .asciz "PowerPC" 16 | .balign 4 17 | 18 | 19 | # note descriptor 20 | 21 | # real mode (-1) or virtual mode (0) 22 | .long 0 23 | 24 | # real-base 25 | .long -1 26 | # real-size 27 | .long -1 28 | 29 | # virt-base 30 | .long -1 31 | # virt-size 32 | .long -1 33 | # load-base 34 | .long 0x4000 35 | 36 | # second note is for IBM LPARs 37 | # length of name 38 | .long 24 39 | # note descriptor size 40 | .long 28 41 | # note type 42 | .long 0x12759999 43 | # name of owner 44 | .asciz "IBM,RPA-Client-Config" 45 | .balign 4 46 | 47 | # lpar affinity 48 | .long 0 49 | # minimum size in megabytes 50 | .long 64 51 | # minimum percentage size 52 | .long 0 53 | # max pft size ( 2^48 bytes ) 54 | .long 48 55 | # splpar 56 | .long 1 57 | # min load ? 58 | .long -1 59 | # new mem def ? 60 | .long 0 61 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | SOURCES_C := src/main.c 2 | SOURCES_AS := src/ofwmagic.s 3 | OBJECTS := $(SOURCES_C:src/%.c=bin/%.c.o) $(SOURCES_AS:src/%.s=bin/%.s.o) 4 | 5 | RELOC := E00000 6 | 7 | CFLAGS := -target powerpc-none-eabi -static -msoft-float -ffreestanding 8 | ASFLAGS := -filetype=obj --arch=ppc32 9 | LDFLAGS := -N --Bstatic -nostdlib -e _start -Ttext ${RELOC} -v 10 | 11 | .PHONY: clean qemu 12 | 13 | all: bin/hellorld.iso 14 | 15 | bin/hellorld.iso: bin/boot.elf boot.tbxi bootinfo.txt hfs.map 16 | #dd if=/dev/zero of=bin/hellorld.dmg bs=1M count=16 status=progress 17 | ##parted bin/hellorld.dmg --script mklabel mac mkpart primary hfs 2048s 100% 18 | #mkdir -p dmg_mount 19 | ##sudo ./kpartx.sh 20 | #sudo mkfs.hfs -v Hellorld bin/hellorld.dmg 21 | #sudo mount -o loop bin/hellorld.dmg ./dmg_mount/ 22 | #sudo mkdir -p ./dmg_mount/ppc 23 | #sudo cp bootinfo.txt ./dmg_mount/ppc 24 | #sudo cp bin/boot.elf ./dmg_mount/ppc 25 | #sudo umount ./dmg_mount/ 26 | #sudo kpartx -d bin/hellorld.dmg 27 | mkdir -p ./dmg_mount/ppc 28 | cp bootinfo.txt boot.tbxi bin/boot.elf ./dmg_mount/ppc 29 | genisoimage \ 30 | -joliet-long -r \ 31 | -V 'Hellorld' \ 32 | -o bin/hellorld.iso \ 33 | --iso-level 4 \ 34 | --netatalk -hfs -probe \ 35 | -map hfs.map \ 36 | -hfs-parms MAX_XTCSIZE=2656248 \ 37 | --chrp-boot \ 38 | -part -no-desktop \ 39 | -hfs-bless ppc \ 40 | -hfs-volid Hellorld_boot \ 41 | ./dmg_mount/ 42 | 43 | 44 | bin/boot.elf: $(OBJECTS) 45 | ld.lld $(OBJECTS) $(LDFLAGS) -o bin/boot.elf 46 | 47 | bin/%.c.o: src/%.c Makefile 48 | clang $< $(CFLAGS) -c -o $@ 49 | 50 | bin/%.s.o: src/%.s Makefile 51 | llvm-mc $< $(ASFLAGS) -o $@ 52 | 53 | clean: 54 | rm -f bin/hellorld.dmg 55 | rm -f bin/*.o 56 | 57 | qemu: bin/hellorld.iso 58 | qemu-system-ppc -L openbios-ppc -boot d -M mac99 -m 256 -cdrom bin/hellorld.iso -device VGA,edid=on 59 | -------------------------------------------------------------------------------- /src/main.c: -------------------------------------------------------------------------------- 1 | static int stack[8192/4 + 4] __attribute__((__used__)); 2 | 3 | typedef long CICell; 4 | 5 | struct CIArgs { 6 | char *service; 7 | CICell nArgs; 8 | CICell nReturns; 9 | 10 | union { 11 | struct { // nArgs=1 + args, nReturns=1 + rets 12 | const char *forth; 13 | CICell cells[6 + 1 + 6]; 14 | } interpret; 15 | 16 | struct { // nArgs=2 + args, nReturns=1 + rets 17 | const char *method; 18 | CICell iHandle; 19 | CICell cells[6 + 1 + 6]; 20 | } callMethod; 21 | 22 | struct { // nArgs=1, nReturns=1 ( device-specifier -- ihandle ) 23 | char *devSpec; // IN parameter 24 | CICell ihandle; // RETURN value 25 | } open; 26 | 27 | struct { // nArgs=1, nReturns=0 ( ihandle -- ) 28 | CICell ihandle; // IN parameter 29 | } close; 30 | 31 | struct { // nArgs=3, nReturns=1 ( ihandle addr length -- actual ) 32 | CICell ihandle; 33 | CICell addr; 34 | CICell length; 35 | CICell actual; 36 | } read; 37 | 38 | struct { // nArgs=3, nReturns=1 ( ihandle addr length -- actual ) 39 | CICell ihandle; 40 | CICell addr; 41 | CICell length; 42 | CICell actual; 43 | } write; 44 | 45 | struct { // nArgs=3, nReturns=1 ( ihandle pos.high pos.low -- result ) 46 | CICell ihandle; 47 | CICell pos_high; 48 | CICell pos_low; 49 | CICell result; 50 | } seek; 51 | 52 | struct { // nArgs=3, nReturns=1 53 | CICell virt; 54 | CICell size; 55 | CICell align; 56 | CICell baseaddr; 57 | } claim; 58 | 59 | struct { // nArgs=2, nReturns=0 60 | CICell virt; 61 | CICell size; 62 | } release; 63 | 64 | struct { // nArgs=1, nReturns=1 ( phandle -- peer-phandle ) 65 | CICell phandle; // IN parameter 66 | CICell peerPhandle; // RETURN value 67 | } peer; 68 | 69 | struct { // nArgs=1, nReturns=1 ( phandle -- child-phandle ) 70 | CICell phandle; // IN parameter 71 | CICell childPhandle; // RETURN value 72 | } child; 73 | 74 | struct { // nArgs=1, nReturns=1 ( phandle -- parent-phandle ) 75 | CICell childPhandle; // IN parameter 76 | CICell parentPhandle; // RETURN value 77 | } parent; 78 | 79 | struct { // nArgs=1, nReturns=1 ( devSpec -- phandle ) 80 | char *devSpec; // IN parameter 81 | CICell phandle; // RETURN value 82 | } finddevice; 83 | 84 | struct { // nArgs=3, nReturns=1 ( ihandle buf buflen -- length ) 85 | CICell ihandle; // IN ihandle 86 | char *buf; // IN buf 87 | CICell buflen; // IN buflen 88 | CICell length; // RETURN length 89 | } instanceToPath; 90 | 91 | struct { // nArgs=1, nReturns=1 ( ihandle -- phandle ) 92 | CICell ihandle; // IN ihandle 93 | CICell phandle; // RETURN phandle 94 | } instanceToPackage; 95 | 96 | struct { // nArgs=3, nReturns=1 ( phandle buf buflen -- length ) 97 | CICell phandle; // IN phandle 98 | char *buf; // IN buf 99 | CICell buflen; // IN buflen 100 | CICell length; // RETURN length 101 | } packageToPath; 102 | 103 | struct { // nArgs=2, nReturns=1 ( phandle name -- size ) 104 | CICell phandle; // IN parameter 105 | char *name; // IN parameter 106 | CICell size; // RETURN value 107 | } getproplen; 108 | 109 | struct { // nArgs=4, nReturns=1 ( phandle name buf buflen -- size ) 110 | CICell phandle; // IN parameter 111 | char *name; // IN parameter 112 | char *buf; // IN parameter 113 | CICell buflen; // IN parameter 114 | CICell size; // RETURN value 115 | } getprop; 116 | 117 | struct { // nArgs=3, nReturns=1 ( phandle previous buf -- flag ) 118 | CICell phandle; // IN parameter 119 | char *previous; // IN parameter 120 | char *buf; // IN parameter 121 | CICell flag; // RETURN value 122 | } nextprop; 123 | 124 | struct { // nArgs=4, nReturns=1 ( phandle name buf buflen -- size ) 125 | CICell phandle; // IN parameter 126 | char *name; // IN parameter 127 | char *buf; // IN parameter 128 | CICell buflen; // IN parameter 129 | CICell size; // RETURN value 130 | } setprop; 131 | 132 | struct { // nArgs=1, nReturns=0 133 | char *bootspec; 134 | } boot; 135 | } args; 136 | }; 137 | typedef struct CIArgs CIArgs; 138 | 139 | typedef long (*ClientInterfacePtr)(CIArgs *args); 140 | 141 | ClientInterfacePtr gClientInterface; 142 | 143 | static void main(); 144 | void startup(void *vpd, int res, ClientInterfacePtr openfirm, char *arg, int argl); 145 | 146 | __asm( 147 | " .text \n" 148 | " .globl _start \n" 149 | "_start: \n" 150 | " sync \n" 151 | " isync \n" 152 | " lis %r1,stack@ha \n" 153 | " addi %r1,%r1,stack@l \n" 154 | " addi %r1,%r1,8192 \n" 155 | " \n" 156 | " mfmsr %r8 \n" 157 | " li %r0,0 \n" 158 | " mtmsr %r0 \n" 159 | " isync \n" 160 | " \n" 161 | " \n" /* test for 601 */ 162 | " mfspr %r0,287 \n" /* mfpvbr %r0 PVR = 287 */ 163 | " srwi %r0,%r0,0x10 \n" 164 | " cmplwi %r0,0x02 \n" /* 601 CPU = 0x0001 */ 165 | " blt 2f \n" /* skip over non-601 BAT setup */ 166 | " cmplwi %r0,0x39 \n" /* PPC970 */ 167 | " blt 0f \n" 168 | " cmplwi %r0,0x45 \n" /* PPC970GX */ 169 | " ble 1f \n" 170 | /* non PPC 601 BATs */ 171 | "0: li %r0,0 \n" 172 | " mtibatu 0,%r0 \n" 173 | " mtibatu 1,%r0 \n" 174 | " mtibatu 2,%r0 \n" 175 | " mtibatu 3,%r0 \n" 176 | " mtdbatu 0,%r0 \n" 177 | " mtdbatu 1,%r0 \n" 178 | " mtdbatu 2,%r0 \n" 179 | " mtdbatu 3,%r0 \n" 180 | " \n" 181 | " li %r9,0x12 \n" /* BATL(0, BAT_M, BAT_PP_RW) */ 182 | " mtibatl 0,%r9 \n" 183 | " mtdbatl 0,%r9 \n" 184 | " li %r9,0x1ffe \n" /* BATU(0, BAT_BL_256M, BAT_Vs) */ 185 | " mtibatu 0,%r9 \n" 186 | " mtdbatu 0,%r9 \n" 187 | " b 3f \n" 188 | /* 970 initialization stuff */ 189 | "1: \n" 190 | /* make sure we're in bridge mode */ 191 | " clrldi %r8,%r8,3 \n" 192 | " mtmsrd %r8 \n" 193 | " isync \n" 194 | /* clear HID5 DCBZ bits (56/57), need to do this early */ 195 | " mfspr %r9,0x3f6 \n" 196 | " rldimi %r9,0,6,56 \n" 197 | " sync \n" 198 | " mtspr 0x3f6,%r9 \n" 199 | " isync \n" 200 | " sync \n" 201 | /* Setup HID1 features, prefetch + i-cacheability controlled by PTE */ 202 | " mfspr %r9,0x3f1 \n" 203 | " li %r11,0x1200 \n" 204 | " sldi %r11,%r11,44 \n" 205 | " or %r9,%r9,%r11 \n" 206 | " mtspr 0x3f1,%r9 \n" 207 | " isync \n" 208 | " sync \n" 209 | " b 3f \n" 210 | /* PPC 601 BATs */ 211 | "2: li %r0,0 \n" 212 | " mtibatu 0,%r0 \n" 213 | " mtibatu 1,%r0 \n" 214 | " mtibatu 2,%r0 \n" 215 | " mtibatu 3,%r0 \n" 216 | " \n" 217 | " li %r9,0x7f \n" 218 | " mtibatl 0,%r9 \n" 219 | " li %r9,0x1a \n" 220 | " mtibatu 0,%r9 \n" 221 | " \n" 222 | " lis %r9,0x80 \n" 223 | " addi %r9,%r9,0x7f \n" 224 | " mtibatl 1,%r9 \n" 225 | " lis %r9,0x80 \n" 226 | " addi %r9,%r9,0x1a \n" 227 | " mtibatu 1,%r9 \n" 228 | " \n" 229 | " lis %r9,0x100 \n" 230 | " addi %r9,%r9,0x7f \n" 231 | " mtibatl 2,%r9 \n" 232 | " lis %r9,0x100 \n" 233 | " addi %r9,%r9,0x1a \n" 234 | " mtibatu 2,%r9 \n" 235 | " \n" 236 | " lis %r9,0x180 \n" 237 | " addi %r9,%r9,0x7f \n" 238 | " mtibatl 3,%r9 \n" 239 | " lis %r9,0x180 \n" 240 | " addi %r9,%r9,0x1a \n" 241 | " mtibatu 3,%r9 \n" 242 | " \n" 243 | "3: isync \n" 244 | " \n" 245 | " mtmsr %r8 \n" 246 | " isync \n" 247 | " \n" 248 | /* 249 | * Make sure that .bss is zeroed 250 | */ 251 | " \n" 252 | " li %r0,0 \n" 253 | " lis %r8,_edata@ha \n" 254 | " addi %r8,%r8,_edata@l\n" 255 | " lis %r9,_end@ha \n" 256 | " addi %r9,%r9,_end@l \n" 257 | " \n" 258 | "5: cmpw 0,%r8,%r9 \n" 259 | " bge 6f \n" 260 | /* 261 | * clear by bytes to avoid ppc601 alignment exceptions 262 | */ 263 | " stb %r0,0(%r8) \n" 264 | " stb %r0,1(%r8) \n" 265 | " stb %r0,2(%r8) \n" 266 | " stb %r0,3(%r8) \n" 267 | " addi %r8,%r8,4 \n" 268 | " b 5b \n" 269 | " \n" 270 | "6: b startup \n" 271 | ); 272 | 273 | void startup(void *vpd, int res, ClientInterfacePtr openfirm, char *arg, int argl) 274 | { 275 | (void) vpd; 276 | (void) res; 277 | (void) arg; 278 | (void) argl; 279 | gClientInterface = openfirm; 280 | //setup(); 281 | main(); 282 | //OF_exit(); 283 | } 284 | 285 | static void main() // __section(".text") 286 | { 287 | //gClientInterface = ciPtr; 288 | 289 | CIArgs args; 290 | args.service = "interpret"; 291 | args.nArgs = 1; 292 | args.nReturns = 1; 293 | args.args.interpret.forth = ".( Hellorld!)"; 294 | 295 | long ret = (*gClientInterface)(&args); 296 | (void) ret; 297 | 298 | do {} while(1); 299 | } 300 | --------------------------------------------------------------------------------