commit 8af9f1eb9514a55630be445fcab4e84b36fde9f6 Author: Hector Martin Date: Wed Jan 13 03:22:11 2021 +0900 Initial commit Signed-off-by: Hector Martin diff --git a/.clang-format b/.clang-format new file mode 100644 index 00000000..e3a05c4f --- /dev/null +++ b/.clang-format @@ -0,0 +1,7 @@ +BasedOnStyle: LLVM +IndentWidth: 4 +UseTab: Never +BreakBeforeBraces: Linux +AllowShortIfStatementsOnASingleLine: Never +AllowShortFunctionsOnASingleLine: false +IndentCaseLabels: true diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..de589d0b --- /dev/null +++ b/.editorconfig @@ -0,0 +1,14 @@ +root = true + +# Defaults +[*] +indent_style = space +indent_size = 4 +tab_width = 4 +charset = utf-8 +insert_final_newline = true +max_line_length = 100 + +[Makefile*] +indent_style = tab +indent_size = 8 diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..f56e2914 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +!build/.keep +build/ diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..fbf3eff8 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "artwork"] + path = artwork + url = https://github.com/AsahiLinux/artwork.git diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..e9bf3772 --- /dev/null +++ b/LICENSE @@ -0,0 +1,20 @@ +Copyright (c) 2021 The Asahi Linux controbutors + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..a494a12e --- /dev/null +++ b/Makefile @@ -0,0 +1,66 @@ +ARCH := aarch64-linux-gnu- + +CFLAGS := -O2 -Wall -Wundef -Werror=strict-prototypes -fno-common -fno-PIE \ + -Werror=implicit-function-declaration -Werror=implicit-int \ + -ffreestanding -mabi=lp64 -fpic +LDFLAGS := -T m1n1.ld -EL -maarch64elf --no-undefined -X -shared -Bsymbolic \ + -z notext --no-apply-dynamic-relocs --orphan-handling=warn --strip-debug \ + -z nocopyreloc \ + +OBJECTS := bootlogo_128.o bootlogo_256.o fb.o main.o start.o startup.o \ + string.o uart.o utils.o utils_asm.o vsprintf.o + +BUILD_OBJS := $(patsubst %,build/%,$(OBJECTS)) +NAME := m1n1 +TARGET := m1n1.macho + +DEPDIR := build/.deps + +CC := $(ARCH)gcc +AS := $(ARCH)gcc +LD := $(ARCH)ld +OBJCOPY := $(ARCH)objcopy + +.PHONY: all clean format +all: build/$(TARGET) +clean: + rm -rf build/* +format: + clang-format -i src/*.c src/*.h + +build/%.o: src/%.S + @echo " AS $@" + @mkdir -p $(DEPDIR) + @$(AS) -c $(CFLAGS) -Wp,-MMD,$(DEPDIR)/$(*F).d,-MQ,"$@",-MP -o $@ $< + +build/%.o: src/%.c + @echo " CC $@" + @mkdir -p $(DEPDIR) + @$(CC) -c $(CFLAGS) -Wp,-MMD,$(DEPDIR)/$(*F).d,-MQ,"$@",-MP -o $@ $< + +build/$(NAME).elf: $(BUILD_OBJS) m1n1.ld + @echo " LD $@" + @$(LD) $(LDFLAGS) -o $@ $(BUILD_OBJS) + +build/$(NAME).macho: build/$(NAME).elf + @echo " MACHO $@" + @$(OBJCOPY) -O binary $< $@ + +build/build_tag.h: + @echo " TAG $@" + @echo "#define BUILD_TAG \"$$(git describe --always --dirty)\"" > $@ + +build/%.bin: data/%.png + @echo " IMG $@" + @convert $< -background black -flatten -depth 8 rgba:$@ + +build/%.o: build/%.bin + @echo " BIN $@" + @$(OBJCOPY) -I binary -O elf64-littleaarch64 $< $@ + +build/main.o: build/build_tag.h src/main.c + +-include $(DEPDIR)/* + + + diff --git a/README.md b/README.md new file mode 100644 index 00000000..6c5d81c9 --- /dev/null +++ b/README.md @@ -0,0 +1,16 @@ +# m1n1: an experimentation playground for Apple Silicon + +(And perhaps some day a Linux bootloader) + +## License + +Copyright (c) 2021 The Asahi Linux contributors + +m1n1 is licensed under the MIT license, as included in the [LICENSE](LICENSE) file. + +Please see the Git history for authorship information. + +Portions of m1n1 are based on mini: +Copyright (c) 2008-2010 Hector Martin "marcan" +Copyright (c) 2008-2010 Sven Peter +Copyright (c) 2008-2010 Andre Heider diff --git a/artwork b/artwork new file mode 160000 index 00000000..35626c31 --- /dev/null +++ b/artwork @@ -0,0 +1 @@ +Subproject commit 35626c31f5c5a8a884aaff93f864795026495742 diff --git a/data/bootlogo_128.png b/data/bootlogo_128.png new file mode 120000 index 00000000..ca3d505e --- /dev/null +++ b/data/bootlogo_128.png @@ -0,0 +1 @@ +../artwork/logos/png_128/AsahiLinux_logomark.png \ No newline at end of file diff --git a/data/bootlogo_256.png b/data/bootlogo_256.png new file mode 120000 index 00000000..99b04e7a --- /dev/null +++ b/data/bootlogo_256.png @@ -0,0 +1 @@ +../artwork/logos/png_256/AsahiLinux_logomark.png \ No newline at end of file diff --git a/m1n1.ld b/m1n1.ld new file mode 100644 index 00000000..b53239d6 --- /dev/null +++ b/m1n1.ld @@ -0,0 +1,185 @@ +ENTRY(_start) + +/* Fake virtual load address for the mach-o */ +_va_base = 0xFFFFFE0007004000; + +/* We are actually relocatable */ +_base = 0; + +_stack_size = 0x20000; + +. = _base; + +PHDRS +{ + hdr PT_LOAD; + text PT_LOAD; + rodata PT_LOAD; + data PT_LOAD; +} + +SECTIONS { + .header : { + _mach_header = .; + /* mach-o header */ + LONG(0xfeedfacf); /* magic */ + LONG(0x100000c); /* cputype */ + LONG(0x02); /* cputype */ + LONG(0x0c); /* filetype */ + LONG(5); /* ncmds */ + LONG(_cmd_end - _cmd_start); /* sizeofcmds */ + LONG(4); /* flags */ + LONG(0); /* reserved */ + + _cmd_start = .; + + /* unix_thread (entrypoint) */ + LONG(0x5); /* type = UNIX_THREAD */ + LONG(0x120); /* cmdsize */ + LONG(6); /* ARM_THREAD64 */ + LONG(0x44); /* length */ + . += 32 * 8; /* useless registers */ + QUAD(_start + _va_off) /* pc */ + . += 8; /* useless registers */ + + ASSERT(. - _cmd_start == 0x120, "Bad unix_thread structure"); + + /* segment: mach-o structures */ + LONG(0x19); /* type = SEGMENT_64 */ + LONG(0x48); /* cmdsize */ + LONG(0x5244485f); /* segname = "_HDR" */ + . += 12; + QUAD(ADDR(.header) + _va_off); /* vmaddr */ + QUAD(SIZEOF(.header)); /* vmsize */ + QUAD(ADDR(.header) - _base); /* fileoff */ + QUAD(SIZEOF(.header)); /* filesize */ + LONG(PROT_READ); /* maxprot */ + LONG(PROT_READ); /* initprot */ + LONG(0); /* nsects */ + LONG(0); /* flags */ + + /* segment: text */ + LONG(0x19); /* type = SEGMENT_64 */ + LONG(0x48); /* cmdsize */ + LONG(0x54584554); /* segname = "TEXT" */ + . += 12; + QUAD(ADDR(.init) + _va_off); /* vmaddr */ + QUAD(_text_size); /* vmsize */ + QUAD(ADDR(.init) - _base); /* fileoff */ + QUAD(_text_size); /* filesize */ + LONG(PROT_READ | PROT_EXECUTE); /* maxprot */ + LONG(PROT_READ | PROT_EXECUTE); /* initprot */ + LONG(0); /* nsects */ + LONG(0); /* flags */ + + /* segment: rodata */ + LONG(0x19); /* type = SEGMENT_64 */ + LONG(0x48); /* cmdsize */ + LONG(0x41444F52); /* segname = "RODA" */ + . += 12; + QUAD(ADDR(.rodata) + _va_off); /* vmaddr */ + QUAD(_rodata_end - ADDR(.rodata)); /* vmsize */ + QUAD(ADDR(.rodata) - _base); /* fileoff */ + QUAD(_rodata_end - ADDR(.rodata)); /* filesize */ + LONG(PROT_READ); /* maxprot */ + LONG(PROT_READ); /* initprot */ + LONG(0); /* nsects */ + LONG(0); /* flags */ + + /* segment: data */ + LONG(0x19); /* type = SEGMENT_64 */ + LONG(0x48); /* cmdsize */ + LONG(0x41544144); /* segmname = "DATA" */ + . += 12; + QUAD(ADDR(.data) + _va_off); /* vmaddr */ + QUAD(_data_size); /* vmsize */ + QUAD(ADDR(.data) - _base); /* fileoff */ + QUAD(SIZEOF(.data)); /* filesize */ + LONG(PROT_READ | PROT_WRITE); /* maxprot */ + LONG(PROT_READ | PROT_WRITE); /* initprot */ + LONG(0); /* nsects */ + LONG(0); /* flags */ + + _cmd_end = .; + + . = ALIGN(0x8000); + _hdr_end = .; + } :hdr + _text_start = .; + .init : ALIGN(0x8000) { + *(.init) + *(.init.*) + } :text + .text : ALIGN(0x8000) { + *(.text) + *(.text.*) + . = ALIGN(8); + *(.got.plt) + . = ALIGN(0x8000); + } :text + _text_size = . - _text_start; + .rodata : ALIGN(0x8000) { + *(.rodata) + *(.rodata.*) + . = ALIGN(8); + } :rodata + .rela.dyn : { + _rela_start = .; + *(.rela) + *(.rela.*) + _rela_end = .; + . = ALIGN(0x8000); + } :rodata + _rodata_end = .; + _data_start = .; + .data : ALIGN(0x8000) { + *(.data) + *(.data.*) + . = ALIGN(8); + _got_start = .; + *(.got) + _got_end = .; + . = ALIGN(0x8000); + } :data + .bss : ALIGN(0x8000) { + *(.bss) + *(.bss.*) + *(.dynbss) + *(COMMON) + . = ALIGN(0x8000); + PROVIDE(_stack_top = .); + . += _stack_size; + PROVIDE(_stack_bot = .); + . = ALIGN(0x8000); + } :data + _data_size = . - _data_start; + _end = .; + + /DISCARD/ : { + *(.discard) + *(.discard.*) + *(.interp .dynamic) + *(.dynsym .dynstr .hash .gnu.hash) + *(.eh_frame) + *(.gnu.version*) + *(.note*) + *(.comment*) + } + + .empty (NOLOAD) : { + *(.plt) *(.plt.*) *(.iplt) *(.igot) + *(.data.rel.ro) + } + ASSERT(SIZEOF(.empty) == 0, "Unexpected sections detected!") + + .got.plt (NOLOAD) : { + *(.got.plt) + } + ASSERT(SIZEOF(.got.plt) == 0 || SIZEOF(.got.plt) == 0x18, "Unexpected GOT PLT detected!") +} + +PROT_READ = 0x01; +PROT_WRITE = 0x02; +PROT_EXECUTE = 0x04; + +_va_off = _va_base - _base; diff --git a/src/fb.c b/src/fb.c new file mode 100644 index 00000000..30a1ac61 --- /dev/null +++ b/src/fb.c @@ -0,0 +1,62 @@ +/* SPDX-License-Identifier: MIT */ + +#include "fb.h" +#include "utils.h" +#include "xnuboot.h" + +u32 *fb; +int fb_s, fb_w, fb_h; + +static u32 *logo; +static int logo_w, logo_h; + +extern char _binary_build_bootlogo_128_bin_start[0]; +extern char _binary_build_bootlogo_256_bin_start[0]; + +void fb_init(void) +{ + fb = (void *)cur_boot_args.video.base; + fb_s = cur_boot_args.video.stride / 4; + fb_w = cur_boot_args.video.width; + fb_h = cur_boot_args.video.height; + printf("fb init: %dx%d [s=%d] @%p\n", fb_w, fb_h, fb_s, fb); + + if (fb_w > 2048) { // random guess + logo = (void *)_binary_build_bootlogo_256_bin_start; + logo_w = logo_h = 256; + } else { + logo = (void *)_binary_build_bootlogo_128_bin_start; + logo_w = logo_h = 128; + } +} + +static inline uint32_t rgbx_to_rgb30(u32 c) +{ + u8 r = c; + u8 g = c >> 8; + u8 b = c >> 16; + return (b << 2) | (g << 12) | (r << 22); +} + +void fb_blit(int x, int y, int w, int h, void *data, int stride) +{ + uint32_t *p = data; + + for (int i = 0; i < h; i++) + for (int j = 0; j < w; j++) + fb[x + j + (y + i) * fb_s] = rgbx_to_rgb30(p[j + i * stride]); +} + +void fb_fill(int x, int y, int w, int h, u32 color) +{ + for (int i = 0; i < h; i++) + for (int j = 0; j < w; j++) + fb[x + j + (y + i) * fb_s] = rgbx_to_rgb30(color); +} + +void fb_display_logo(void) +{ + printf("fb: display logo\n"); + fb_blit((fb_w - logo_w) / 2, (fb_h - logo_h) / 2, logo_w, logo_h, logo, + logo_w); +} diff --git a/src/fb.h b/src/fb.h new file mode 100644 index 00000000..4ea137c4 --- /dev/null +++ b/src/fb.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: MIT */ + +#ifndef FB_H +#define FB_H + +#include "types.h" + +extern u32 *fb; +extern int fb_s, fb_w, fb_h; + +void fb_init(void); +void fb_blit(int x, int y, int w, int h, void *data, int stride); +void fb_fill(int x, int y, int w, int h, u32 color); + +void fb_display_logo(void); + +#endif diff --git a/src/main.c b/src/main.c new file mode 100644 index 00000000..f7312be7 --- /dev/null +++ b/src/main.c @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: MIT */ + +#include "fb.h" +#include "utils.h" +#include "xnuboot.h" + +#include "../build/build_tag.h" + +void m1n1_main(void) +{ + printf("\n\nm1n1 v%s\n", BUILD_TAG); + printf("Copyright (C) 2021 The Asahi Linux Contributors\n"); + printf("Licensed under the MIT license\n"); + + fb_init(); + fb_display_logo(); + + u64 dtaddr = ((u64)cur_boot_args.devtree) - cur_boot_args.virt_base + + cur_boot_args.phys_base; + + hexdump((void *)dtaddr, cur_boot_args.devtree_size); + + while (1) + ; +} diff --git a/src/start.S b/src/start.S new file mode 100644 index 00000000..6c101b88 --- /dev/null +++ b/src/start.S @@ -0,0 +1,61 @@ +/* SPDX-License-Identifier: MIT */ + +#define UART_BASE 0x235200000 +#define UTRSTAT 0x010 +#define UTXH 0x020 + +.extern _start_c +.extern _stack_bot + +.section .init, "ax" + +.globl _start +.type _start, @function +_start: + mov x9, x0 + + mov w0, 'm' + bl debug_putc + + mov w0, '1' + bl debug_putc + + adrp x1, _stack_bot + mov sp, x1 + + mov w0, 'n' + bl debug_putc + + adrp x0, _base + mov x10, x0 + adrp x1, _rela_start + add x1, x1, :lo12:_rela_start + adrp x2, _rela_end + add x2, x2, :lo12:_rela_end + bl apply_rela + + mov w0, '1' + bl debug_putc + mov w0, '\r' + bl debug_putc + mov w0, '\n' + bl debug_putc + + mov x0, x9 + mov x1, x10 + bl _start_c + b . + +.globl debug_putc +.type debug_putc, @function +debug_putc: + ldr x1, =UART_BASE + +1: + ldr w2, [x1, UTRSTAT] + tst w2, #2 + beq 1b + str w0, [x1, UTXH] + ret + + .pool diff --git a/src/startup.c b/src/startup.c new file mode 100644 index 00000000..0cda51f8 --- /dev/null +++ b/src/startup.c @@ -0,0 +1,77 @@ +/* SPDX-License-Identifier: MIT */ + +#include "string.h" +#include "types.h" +#include "uart.h" +#include "utils.h" +#include "xnuboot.h" + +struct boot_args cur_boot_args; + +struct rela_entry { + uint64_t off, type, addend; +}; + +void debug_putc(char c); +void m1n1_main(void); + +#define R_AARCH64_RELATIVE 1027 + +void apply_rela(uint64_t base, struct rela_entry *rela_start, + struct rela_entry *rela_end) +{ + struct rela_entry *e = rela_start; + + while (e < rela_end) { + switch (e->type) { + case R_AARCH64_RELATIVE: + *(u64 *)(base + e->off) = base + e->addend; + break; + default: + debug_putc('R'); + debug_putc('!'); + while (1) + ; + } + e++; + } +} + +void dump_boot_args(struct boot_args *ba) +{ + printf(" revision: %d\n", ba->revision); + printf(" version: %d\n", ba->version); + printf(" virt_base: 0x%lx\n", ba->virt_base); + printf(" phys_base: 0x%lx\n", ba->phys_base); + printf(" mem_size: 0x%lx\n", ba->mem_size); + printf(" top_of_kdata: 0x%lx\n", ba->top_of_kernel_data); + printf(" video:\n"); + printf(" base: 0x%lx\n", ba->video.base); + printf(" display: 0x%lx\n", ba->video.display); + printf(" stride: 0x%lx\n", ba->video.stride); + printf(" width: %d\n", ba->video.width); + printf(" height: %d\n", ba->video.height); + printf(" depth: 0x%lx\n", ba->video.depth); + printf(" machine_type: %d\n", ba->machine_type); + printf(" devtree: %p\n", ba->devtree); + printf(" devtree_size: 0x%x\n", ba->devtree_size); + printf(" cmdline: %s\n", ba->cmdline); + printf(" boot_flags: 0x%x\n", ba->boot_flags); + printf(" mem_size_act: 0x%lx\n", ba->mem_size_actual); +} + +void _start_c(void *boot_args, void *base) +{ + uart_putc('s'); + uart_init(); + uart_putc('c'); + uart_puts(": Initializing\n"); + + printf("boot_args at %p\n", boot_args); + + memcpy(&cur_boot_args, boot_args, sizeof(cur_boot_args)); + + dump_boot_args(&cur_boot_args); + + m1n1_main(); +} diff --git a/src/string.c b/src/string.c new file mode 100644 index 00000000..2e42c1ed --- /dev/null +++ b/src/string.c @@ -0,0 +1,86 @@ +/* SPDX-License-Identifier: MIT */ + +#include + +// Routines based on The Public Domain C Library + +void *memcpy(void *s1, const void *s2, size_t n) +{ + char *dest = (char *)s1; + const char *src = (const char *)s2; + + while (n--) { + *dest++ = *src++; + } + + return s1; +} + +int memcmp(const void *s1, const void *s2, size_t n) +{ + const unsigned char *p1 = (const unsigned char *)s1; + const unsigned char *p2 = (const unsigned char *)s2; + + while (n--) { + if (*p1 != *p2) { + return *p1 - *p2; + } + + ++p1; + ++p2; + } + + return 0; +} + +void *memset(void *s, int c, size_t n) +{ + unsigned char *p = (unsigned char *)s; + + while (n--) { + *p++ = (unsigned char)c; + } + + return s; +} + +char *strcpy(char *s1, const char *s2) +{ + char *rc = s1; + + while ((*s1++ = *s2++)) { + /* EMPTY */ + } + + return rc; +} + +char *strncpy(char *s1, const char *s2, size_t n) +{ + char *rc = s1; + + while (n && (*s1++ = *s2++)) { + /* Cannot do "n--" in the conditional as size_t is unsigned and we have + to check it again for >0 in the next loop below, so we must not risk + underflow. + */ + --n; + } + + /* Checking against 1 as we missed the last --n in the loop above. */ + while (n-- > 1) { + *s1++ = '\0'; + } + + return rc; +} + +int strcmp(const char *s1, const char *s2) +{ + while ((*s1) && (*s1 == *s2)) { + ++s1; + ++s2; + } + + return (*(unsigned char *)s1 - *(unsigned char *)s2); +} diff --git a/src/string.h b/src/string.h new file mode 100644 index 00000000..76640849 --- /dev/null +++ b/src/string.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: MIT */ + +#ifndef STRING_H +#define STRING_H + +#include + +void *memcpy(void *s1, const void *s2, size_t n); +int memcmp(const void *s1, const void *s2, size_t n); +void *memset(void *s, int c, size_t n); +char *strcpy(char *s1, const char *s2); +char *strncpy(char *s1, const char *s2, size_t n); +int strcmp(const char *s1, const char *s2); + +#endif diff --git a/src/types.h b/src/types.h new file mode 100644 index 00000000..f5823c4c --- /dev/null +++ b/src/types.h @@ -0,0 +1,49 @@ +/* SPDX-License-Identifier: MIT */ + +#ifndef TYPES_H +#define TYPES_H + +#include +#include + +typedef uint8_t u8; +typedef uint16_t u16; +typedef uint32_t u32; +typedef uint64_t u64; + +typedef int8_t s8; +typedef int16_t s16; +typedef int32_t s32; +typedef int64_t s64; + +typedef u64 uintptr_t; +typedef s64 ptrdiff_t; + +#define NULL ((void *)0) + +#define ALIGNED(x) __attribute__((aligned(x))) + +#define STACK_ALIGN(type, name, cnt, alignment) \ + u8 _al__##name[( \ + (sizeof(type) * (cnt)) + (alignment) + \ + (((sizeof(type) * (cnt)) % (alignment)) > 0 \ + ? ((alignment) - ((sizeof(type) * (cnt)) % (alignment))) \ + : 0))]; \ + type *name = \ + (type *)(((u32)(_al__##name)) + \ + ((alignment) - (((u32)(_al__##name)) & ((alignment)-1)))) + +#define INT_MAX ((int)0x7fffffff) +#define UINT_MAX ((unsigned int)0xffffffff) + +#define LONG_MAX ((long)0x7fffffffffffffffl) +#define ULONG_MAX ((unsigned long)0xfffffffffffffffful) + +#define LLONG_MAX LONG_MAX +#define ULLONG_MAX ULLONG_MAX + +#define HAVE_PTRDIFF_T 1 +#define HAVE_UINTPTR_T 1 +#define UPTRDIFF_T uintptr_t + +#endif diff --git a/src/uart.c b/src/uart.c new file mode 100644 index 00000000..399f54bd --- /dev/null +++ b/src/uart.c @@ -0,0 +1,63 @@ +/* SPDX-License-Identifier: MIT */ + +#include + +#include "types.h" +#include "uart.h" +#include "utils.h" +#include "vsprintf.h" + +#define UART_BASE 0x235200000L + +#define ULCON 0x000 +#define UCON 0x004 +#define UFCON 0x008 +#define UTRSTAT 0x010 +#define UTXH 0x020 +#define URXH 0x024 +#define UBRDIV 0x028 +#define UFRACVAL 0x02c + +void *pxx = uart_init; + +void uart_init(void) +{ + /* keep UART config from iBoot */ +} + +void uart_putbyte(u8 c) +{ + while (!(read32(UART_BASE + UTRSTAT) & 0x02)) + ; + + write32(UART_BASE + UTXH, c); +} + +void uart_putc(u8 c) +{ + if (c == '\n') + uart_putbyte('\r'); + + uart_putbyte(c); +} + +void uart_puts(const char *s) +{ + while (*s) + uart_putc(*(s++)); + + uart_putc('\n'); +} + +void uart_write(const void *buf, size_t count) +{ + const u8 *p = buf; + + while (count--) + uart_putbyte(*p++); +} + +u8 uart_getc(void) +{ + return 0; +} diff --git a/src/uart.h b/src/uart.h new file mode 100644 index 00000000..fbe157c4 --- /dev/null +++ b/src/uart.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: MIT */ + +#ifndef UART_H +#define UART_H + +void uart_init(void); + +void uart_putc(u8 c); +u8 uart_getc(void); + +void uart_write(const void *buf, size_t count); + +void uart_puts(const char *s); + +#endif diff --git a/src/utils.c b/src/utils.c new file mode 100644 index 00000000..b9d0a1f4 --- /dev/null +++ b/src/utils.c @@ -0,0 +1,80 @@ +/* SPDX-License-Identifier: MIT */ + +#include "utils.h" +#include "types.h" +#include "uart.h" +#include "vsprintf.h" + +#include + +static char ascii(char s) +{ + if (s < 0x20) + return '.'; + if (s > 0x7E) + return '.'; + return s; +} + +void hexdump(const void *d, int len) +{ + u8 *data; + int i, off; + data = (u8 *)d; + for (off = 0; off < len; off += 16) { + printf("%08x ", off); + for (i = 0; i < 16; i++) { + if ((i + off) >= len) + printf(" "); + else + printf("%02x ", data[off + i]); + } + + printf(" "); + for (i = 0; i < 16; i++) { + if ((i + off) >= len) + printf(" "); + else + printf("%c", ascii(data[off + i])); + } + printf("\n"); + } +} + +void regdump(u64 addr, int len) +{ + u64 i, off; + for (off = 0; off < len; off += 32) { + printf("%016x ", addr + off); + for (i = 0; i < 32; i += 4) { + printf("%08x ", read32(addr + off + i)); + } + printf("\n"); + } +} + +int sprintf(char *buffer, const char *fmt, ...) +{ + va_list args; + int i; + + va_start(args, fmt); + i = vsprintf(buffer, fmt, args); + va_end(args); + return i; +} + +int debug_printf(const char *fmt, ...) +{ + va_list args; + char buffer[512]; + int i; + + va_start(args, fmt); + i = vsprintf(buffer, fmt, args); + va_end(args); + + uart_write(buffer, i); + + return i; +} diff --git a/src/utils.h b/src/utils.h new file mode 100644 index 00000000..5a21f2c7 --- /dev/null +++ b/src/utils.h @@ -0,0 +1,167 @@ +/* SPDX-License-Identifier: MIT */ + +#ifndef UTILS_H +#define UTILS_H + +#include "types.h" + +#define printf debug_printf + +static inline u32 read32(u64 addr) +{ + u32 data; + __asm__ volatile("ldr\t%w0, [%1]" : "=r"(data) : "r"(addr)); + return data; +} + +static inline void write32(u64 addr, u32 data) +{ + __asm__ volatile("str\t%w0, [%1]" : : "r"(data), "r"(addr)); +} + +static inline u32 set32(u64 addr, u32 set) +{ + u32 data; + __asm__ volatile("ldr\t%w0, [%1]\n" + "\torr\t%w0, %w0, %w2\n" + "\tstr\t%w0, [%1]" + : "=&r"(data) + : "r"(addr), "r"(set)); + return data; +} + +static inline u32 clear32(u64 addr, u32 clear) +{ + u32 data; + __asm__ volatile("ldr\t%w0, [%1]\n" + "\tbic\t%w0, %w0, %w2\n" + "\tstr\t%w0, [%1]" + : "=&r"(data) + : "r"(addr), "r"(clear)); + return data; +} + +static inline u32 mask32(u64 addr, u32 clear, u32 set) +{ + u32 data; + __asm__ volatile("ldr\t%w0, [%1]\n" + "\tbic\t%w0, %w0, %w3\n" + "\torr\t%w0, %w0, %w2\n" + "\tstr\t%w0, [%1]" + : "=&r"(data) + : "r"(addr), "r"(set), "r"(clear)); + return data; +} + +static inline u16 read16(u64 addr) +{ + u32 data; + __asm__ volatile("ldrh\t%w0, [%1]" : "=r"(data) : "r"(addr)); + return data; +} + +static inline void write16(u64 addr, u16 data) +{ + __asm__ volatile("strh\t%w0, [%1]" : : "r"(data), "r"(addr)); +} + +static inline u16 set16(u64 addr, u16 set) +{ + u16 data; + __asm__ volatile("ldrh\t%w0, [%1]\n" + "\torr\t%w0, %w0, %w2\n" + "\tstrh\t%w0, [%1]" + : "=&r"(data) + : "r"(addr), "r"(set) + + ); + return data; +} + +static inline u16 clear16(u64 addr, u16 clear) +{ + u16 data; + __asm__ volatile("ldrh\t%w0, [%1]\n" + "\tbic\t%w0, %w0, %w2\n" + "\tstrh\t%w0, [%1]" + : "=&r"(data) + : "r"(addr), "r"(clear)); + return data; +} + +static inline u16 mask16(u64 addr, u16 clear, u16 set) +{ + u16 data; + __asm__ volatile("ldrh\t%w0, [%1]\n" + "\tbic\t%w0, %3\n" + "\torr\t%w0, %w0, %w2\n" + "\tstrh\t%w0, [%1]" + : "=&r"(data) + : "r"(addr), "r"(set), "r"(clear)); + return data; +} + +static inline u8 read8(u64 addr) +{ + u32 data; + __asm__ volatile("ldrb\t%w0, [%1]" : "=r"(data) : "r"(addr)); + return data; +} + +static inline void write8(u64 addr, u8 data) +{ + __asm__ volatile("strb\t%w0, [%1]" : : "r"(data), "r"(addr)); +} + +static inline u8 set8(u64 addr, u8 set) +{ + u8 data; + __asm__ volatile("ldrb\t%w0, [%1]\n" + "\torr\t%w0, %w0, %w2\n" + "\tstrb\t%w0, [%1]" + : "=&r"(data) + : "r"(addr), "r"(set)); + return data; +} + +static inline u8 clear8(u64 addr, u8 clear) +{ + u8 data; + __asm__ volatile("ldrb\t%w0, [%1]\n" + "\tbic\t%w0, %w0, %w2\n" + "\tstrb\t%w0, [%1]" + : "=&r"(data) + : "r"(addr), "r"(clear)); + return data; +} + +static inline u8 mask8(u64 addr, u8 clear, u8 set) +{ + u8 data; + __asm__ volatile("ldrb\t%w0, [%1]\n" + "\tbic\t%w0, %w0, %w3\n" + "\torr\t%w0, %w0, %w2\n" + "\tstrb\t%w0, [%1]" + : "=&r"(data) + : "r"(addr), "r"(set), "r"(clear)); + return data; +} + +/* + * These functions are guaranteed to copy by reading from src and writing to dst + * in -bit units If size is not aligned, the remaining bytes are not copied + */ +void memset32(void *dst, u32 value, u32 size); +void memcpy32(void *dst, void *src, u32 size); +void memset16(void *dst, u16 value, u32 size); +void memcpy16(void *dst, void *src, u32 size); +void memset8(void *dst, u8 value, u32 size); +void memcpy8(void *dst, void *src, u32 size); + +void hexdump(const void *d, int len); +void regdump(u64 addr, int len); +int sprintf(char *str, const char *fmt, ...); +int debug_printf(const char *fmt, ...); +void udelay(u32 d); + +#endif diff --git a/src/utils_asm.S b/src/utils_asm.S new file mode 100644 index 00000000..a41f57fb --- /dev/null +++ b/src/utils_asm.S @@ -0,0 +1,72 @@ +/* SPDX-License-Identifier: MIT */ + +.text + +.globl memcpy32 +.type memcpy32, @function +memcpy32: + ands x2, x2, #~3 + beq 2f +1: ldr x3, [x1], #4 + str w3, [x0], #4 + subs x2, x2, #4 + bne 1b +2: + ret + +.globl memset32 +.type memset32, @function +memset32: + ands x2, x2, #~3 + beq 2f +1: str w1, [x0], #4 + subs x2, x2, #4 + bne 1b +2: + ret + +.globl memcpy16 +.type memcpy16, @function +memcpy16: + ands x2, x2, #~1 + beq 2f +1: ldrh w3, [x1], #2 + strh w3, [x0], #2 + subs x2, x2, #2 + bne 1b +2: + ret + +.globl memset16 +.type memset16, @function +memset16: + ands x2, x2, #~1 + beq 2f +1: strh w1, [x0], #2 + subs x2, x2, #2 + bne 1b +2: + ret + +.globl memcpy8 +.type memcpy8, @function +memcpy8: + cmp x2, #0 + beq 2f +1: ldrb w3, [x1], #1 + strb w3, [x0], #1 + subs x2, x2, #1 + bne 1b +2: + ret + +.globl memset8 +.type memset8, @function +memset8: + cmp x2, #0 + beq 2f +1: strb w1, [x0], #1 + subs x2, x2, #1 + bne 1b +2: + ret diff --git a/src/vsprintf.c b/src/vsprintf.c new file mode 100644 index 00000000..8805d802 --- /dev/null +++ b/src/vsprintf.c @@ -0,0 +1,708 @@ +/* + * Copyright (c) 1995 Patrick Powell. + * + * This code is based on code written by Patrick Powell . + * It may be used for any purpose as long as this notice remains intact on all + * source code distributions. + */ + +/* + * Copyright (c) 2008 Holger Weiss. + * + * This version of the code is maintained by Holger Weiss . + * My changes to the code may freely be used, modified and/or redistributed for + * any purpose. It would be nice if additions and fixes to this file (including + * trivial code cleanups) would be sent back in order to let me include them in + * the version available at . + * However, this is not a requirement for using or redistributing (possibly + * modified) versions of this file, nor is leaving this notice intact mandatory. + */ + +/* + * History + * + * 2009-03-05 Hector Martin "marcan" + * + * Hacked up and removed a lot of stuff including floating-point support, + * a bunch of ifs and defines, locales, and tests + * + * 2008-01-20 Holger Weiss for C99-snprintf 1.1: + * + * Fixed the detection of infinite floating point values on IRIX (and + * possibly other systems) and applied another few minor cleanups. + * + * 2008-01-06 Holger Weiss for C99-snprintf 1.0: + * + * Added a lot of new features, fixed many bugs, and incorporated various + * improvements done by Andrew Tridgell , Russ Allbery + * , Hrvoje Niksic , Damien Miller + * , and others for the Samba, INN, Wget, and OpenSSH + * projects. The additions include: support the "e", "E", "g", "G", and + * "F" conversion specifiers (and use conversion style "f" or "F" for the + * still unsupported "a" and "A" specifiers); support the "hh", "ll", "j", + * "t", and "z" length modifiers; support the "#" flag and the (non-C99) + * "'" flag; use localeconv(3) (if available) to get both the current + * locale's decimal point character and the separator between groups of + * digits; fix the handling of various corner cases of field width and + * precision specifications; fix various floating point conversion bugs; + * handle infinite and NaN floating point values; don't attempt to write to + * the output buffer (which may be NULL) if a size of zero was specified; + * check for integer overflow of the field width, precision, and return + * values and during the floating point conversion; use the OUTCHAR() macro + * instead of a function for better performance; provide asprintf(3) and + * vasprintf(3) functions; add new test cases. The replacement functions + * have been renamed to use an "rpl_" prefix, the function calls in the + * main project (and in this file) must be redefined accordingly for each + * replacement function which is needed (by using Autoconf or other means). + * Various other minor improvements have been applied and the coding style + * was cleaned up for consistency. + * + * 2007-07-23 Holger Weiss for Mutt 1.5.13: + * + * C99 compliant snprintf(3) and vsnprintf(3) functions return the number + * of characters that would have been written to a sufficiently sized + * buffer (excluding the '\0'). The original code simply returned the + * length of the resulting output string, so that's been fixed. + * + * 1998-03-05 Michael Elkins for Mutt 0.90.8: + * + * The original code assumed that both snprintf(3) and vsnprintf(3) were + * missing. Some systems only have snprintf(3) but not vsnprintf(3), so + * the code is now broken down under HAVE_SNPRINTF and HAVE_VSNPRINTF. + * + * 1998-01-27 Thomas Roessler for Mutt 0.89i: + * + * The PGP code was using unsigned hexadecimal formats. Unfortunately, + * unsigned formats simply didn't work. + * + * 1997-10-22 Brandon Long for Mutt 0.87.1: + * + * Ok, added some minimal floating point support, which means this probably + * requires libm on most operating systems. Don't yet support the exponent + * (e,E) and sigfig (g,G). Also, fmtint() was pretty badly broken, it just + * wasn't being exercised in ways which showed it, so that's been fixed. + * Also, formatted the code to Mutt conventions, and removed dead code left + * over from the original. Also, there is now a builtin-test, run with: + * gcc -DTEST_SNPRINTF -o snprintf snprintf.c -lm && ./snprintf + * + * 2996-09-15 Brandon Long for Mutt 0.43: + * + * This was ugly. It is still ugly. I opted out of floating point + * numbers, but the formatter understands just about everything from the + * normal C string format, at least as far as I can tell from the Solaris + * 2.5 printf(3S) man page. + */ + +#include "types.h" +#include + +#define VA_START(ap, last) va_start(ap, last) +#define VA_SHIFT(ap, value, type) /* No-op for ANSI C. */ + +#define ULLONG unsigned long long +#define UINTMAX_T unsigned long +#define LLONG long +#define INTMAX_T long + +/* Support for uintptr_t. */ +#ifndef UINTPTR_T +#if HAVE_UINTPTR_T || defined(uintptr_t) +#define UINTPTR_T uintptr_t +#else +#define UINTPTR_T unsigned long int +#endif /* HAVE_UINTPTR_T || defined(uintptr_t) */ +#endif /* !defined(UINTPTR_T) */ + +/* Support for ptrdiff_t. */ +#ifndef PTRDIFF_T +#if HAVE_PTRDIFF_T || defined(ptrdiff_t) +#define PTRDIFF_T ptrdiff_t +#else +#define PTRDIFF_T long int +#endif /* HAVE_PTRDIFF_T || defined(ptrdiff_t) */ +#endif /* !defined(PTRDIFF_T) */ + +/* + * We need an unsigned integer type corresponding to ptrdiff_t (cf. C99: + * 7.19.6.1, 7). However, we'll simply use PTRDIFF_T and convert it to an + * unsigned type if necessary. This should work just fine in practice. + */ +#ifndef UPTRDIFF_T +#define UPTRDIFF_T PTRDIFF_T +#endif /* !defined(UPTRDIFF_T) */ + +/* + * We need a signed integer type corresponding to size_t (cf. C99: 7.19.6.1, 7). + * However, we'll simply use size_t and convert it to a signed type if + * necessary. This should work just fine in practice. + */ +#ifndef SSIZE_T +#define SSIZE_T size_t +#endif /* !defined(SSIZE_T) */ + +/* + * Buffer size to hold the octal string representation of UINT128_MAX without + * nul-termination ("3777777777777777777777777777777777777777777"). + */ +#ifdef MAX_CONVERT_LENGTH +#undef MAX_CONVERT_LENGTH +#endif /* defined(MAX_CONVERT_LENGTH) */ +#define MAX_CONVERT_LENGTH 43 + +/* Format read states. */ +#define PRINT_S_DEFAULT 0 +#define PRINT_S_FLAGS 1 +#define PRINT_S_WIDTH 2 +#define PRINT_S_DOT 3 +#define PRINT_S_PRECISION 4 +#define PRINT_S_MOD 5 +#define PRINT_S_CONV 6 + +/* Format flags. */ +#define PRINT_F_MINUS (1 << 0) +#define PRINT_F_PLUS (1 << 1) +#define PRINT_F_SPACE (1 << 2) +#define PRINT_F_NUM (1 << 3) +#define PRINT_F_ZERO (1 << 4) +#define PRINT_F_QUOTE (1 << 5) +#define PRINT_F_UP (1 << 6) +#define PRINT_F_UNSIGNED (1 << 7) +#define PRINT_F_TYPE_G (1 << 8) +#define PRINT_F_TYPE_E (1 << 9) + +/* Conversion flags. */ +#define PRINT_C_CHAR 1 +#define PRINT_C_SHORT 2 +#define PRINT_C_LONG 3 +#define PRINT_C_LLONG 4 +//#define PRINT_C_LDOUBLE 5 +#define PRINT_C_SIZE 6 +#define PRINT_C_PTRDIFF 7 +#define PRINT_C_INTMAX 8 + +#ifndef MAX +#define MAX(x, y) ((x >= y) ? x : y) +#endif /* !defined(MAX) */ +#ifndef CHARTOINT +#define CHARTOINT(ch) (ch - '0') +#endif /* !defined(CHARTOINT) */ +#ifndef ISDIGIT +#define ISDIGIT(ch) ('0' <= (unsigned char)ch && (unsigned char)ch <= '9') +#endif /* !defined(ISDIGIT) */ + +#define OUTCHAR(str, len, size, ch) \ + do { \ + if (len + 1 < size) \ + str[len] = ch; \ + (len)++; \ + } while (/* CONSTCOND */ 0) + +static void fmtstr(char *, size_t *, size_t, const char *, int, int, int); +static void fmtint(char *, size_t *, size_t, INTMAX_T, int, int, int, int); +static void printsep(char *, size_t *, size_t); +static int getnumsep(int); +static int convert(UINTMAX_T, char *, size_t, int, int); + +int vsnprintf(char *str, size_t size, const char *format, va_list args) +{ + INTMAX_T value; + unsigned char cvalue; + const char *strvalue; + INTMAX_T *intmaxptr; + PTRDIFF_T *ptrdiffptr; + SSIZE_T *sizeptr; + LLONG *llongptr; + long int *longptr; + int *intptr; + short int *shortptr; + signed char *charptr; + size_t len = 0; + int overflow = 0; + int base = 0; + int cflags = 0; + int flags = 0; + int width = 0; + int precision = -1; + int state = PRINT_S_DEFAULT; + char ch = *format++; + + /* + * C99 says: "If `n' is zero, nothing is written, and `s' may be a null + * pointer." (7.19.6.5, 2) We're forgiving and allow a NULL pointer + * even if a size larger than zero was specified. At least NetBSD's + * snprintf(3) does the same, as well as other versions of this file. + * (Though some of these versions will write to a non-NULL buffer even + * if a size of zero was specified, which violates the standard.) + */ + if (str == NULL && size != 0) + size = 0; + + while (ch != '\0') + switch (state) { + case PRINT_S_DEFAULT: + if (ch == '%') + state = PRINT_S_FLAGS; + else + OUTCHAR(str, len, size, ch); + ch = *format++; + break; + case PRINT_S_FLAGS: + switch (ch) { + case '-': + flags |= PRINT_F_MINUS; + ch = *format++; + break; + case '+': + flags |= PRINT_F_PLUS; + ch = *format++; + break; + case ' ': + flags |= PRINT_F_SPACE; + ch = *format++; + break; + case '#': + flags |= PRINT_F_NUM; + ch = *format++; + break; + case '0': + flags |= PRINT_F_ZERO; + ch = *format++; + break; + case '\'': /* SUSv2 flag (not in C99). */ + flags |= PRINT_F_QUOTE; + ch = *format++; + break; + default: + state = PRINT_S_WIDTH; + break; + } + break; + case PRINT_S_WIDTH: + if (ISDIGIT(ch)) { + ch = CHARTOINT(ch); + if (width > (INT_MAX - ch) / 10) { + overflow = 1; + goto out; + } + width = 10 * width + ch; + ch = *format++; + } else if (ch == '*') { + /* + * C99 says: "A negative field width argument is + * taken as a `-' flag followed by a positive + * field width." (7.19.6.1, 5) + */ + if ((width = va_arg(args, int)) < 0) { + flags |= PRINT_F_MINUS; + width = -width; + } + ch = *format++; + state = PRINT_S_DOT; + } else + state = PRINT_S_DOT; + break; + case PRINT_S_DOT: + if (ch == '.') { + state = PRINT_S_PRECISION; + ch = *format++; + } else + state = PRINT_S_MOD; + break; + case PRINT_S_PRECISION: + if (precision == -1) + precision = 0; + if (ISDIGIT(ch)) { + ch = CHARTOINT(ch); + if (precision > (INT_MAX - ch) / 10) { + overflow = 1; + goto out; + } + precision = 10 * precision + ch; + ch = *format++; + } else if (ch == '*') { + /* + * C99 says: "A negative precision argument is + * taken as if the precision were omitted." + * (7.19.6.1, 5) + */ + if ((precision = va_arg(args, int)) < 0) + precision = -1; + ch = *format++; + state = PRINT_S_MOD; + } else + state = PRINT_S_MOD; + break; + case PRINT_S_MOD: + switch (ch) { + case 'h': + ch = *format++; + if (ch == 'h') { /* It's a char. */ + ch = *format++; + cflags = PRINT_C_CHAR; + } else + cflags = PRINT_C_SHORT; + break; + case 'l': + ch = *format++; + if (ch == 'l') { /* It's a long long. */ + ch = *format++; + cflags = PRINT_C_LLONG; + } else + cflags = PRINT_C_LONG; + break; + case 'j': + cflags = PRINT_C_INTMAX; + ch = *format++; + break; + case 't': + cflags = PRINT_C_PTRDIFF; + ch = *format++; + break; + case 'z': + cflags = PRINT_C_SIZE; + ch = *format++; + break; + } + state = PRINT_S_CONV; + break; + case PRINT_S_CONV: + switch (ch) { + case 'd': + /* FALLTHROUGH */ + case 'i': + switch (cflags) { + case PRINT_C_CHAR: + value = (signed char)va_arg(args, int); + break; + case PRINT_C_SHORT: + value = (short int)va_arg(args, int); + break; + case PRINT_C_LONG: + value = va_arg(args, long int); + break; + case PRINT_C_LLONG: + value = va_arg(args, LLONG); + break; + case PRINT_C_SIZE: + value = va_arg(args, SSIZE_T); + break; + case PRINT_C_INTMAX: + value = va_arg(args, INTMAX_T); + break; + case PRINT_C_PTRDIFF: + value = va_arg(args, PTRDIFF_T); + break; + default: + value = va_arg(args, int); + break; + } + fmtint(str, &len, size, value, 10, width, precision, + flags); + break; + case 'X': + flags |= PRINT_F_UP; + /* FALLTHROUGH */ + case 'x': + base = 16; + /* FALLTHROUGH */ + case 'o': + if (base == 0) + base = 8; + /* FALLTHROUGH */ + case 'u': + if (base == 0) + base = 10; + flags |= PRINT_F_UNSIGNED; + switch (cflags) { + case PRINT_C_CHAR: + value = + (unsigned char)va_arg(args, unsigned int); + break; + case PRINT_C_SHORT: + value = (unsigned short int)va_arg( + args, unsigned int); + break; + case PRINT_C_LONG: + value = va_arg(args, unsigned long int); + break; + case PRINT_C_LLONG: + value = va_arg(args, ULLONG); + break; + case PRINT_C_SIZE: + value = va_arg(args, size_t); + break; + case PRINT_C_INTMAX: + value = va_arg(args, UINTMAX_T); + break; + case PRINT_C_PTRDIFF: + value = va_arg(args, UPTRDIFF_T); + break; + default: + value = va_arg(args, unsigned int); + break; + } + fmtint(str, &len, size, value, base, width, precision, + flags); + break; + case 'c': + cvalue = va_arg(args, int); + OUTCHAR(str, len, size, cvalue); + break; + case 's': + strvalue = va_arg(args, char *); + fmtstr(str, &len, size, strvalue, width, precision, + flags); + break; + case 'p': + /* + * C99 says: "The value of the pointer is + * converted to a sequence of printing + * characters, in an implementation-defined + * manner." (C99: 7.19.6.1, 8) + */ + if ((strvalue = va_arg(args, void *)) == NULL) + /* + * We use the glibc format. BSD prints + * "0x0", SysV "0". + */ + fmtstr(str, &len, size, "(nil)", width, -1, flags); + else { + /* + * We use the BSD/glibc format. SysV + * omits the "0x" prefix (which we emit + * using the PRINT_F_NUM flag). + */ + flags |= PRINT_F_NUM; + flags |= PRINT_F_UNSIGNED; + fmtint(str, &len, size, (UINTPTR_T)strvalue, 16, + width, precision, flags); + } + break; + case 'n': + switch (cflags) { + case PRINT_C_CHAR: + charptr = va_arg(args, signed char *); + *charptr = len; + break; + case PRINT_C_SHORT: + shortptr = va_arg(args, short int *); + *shortptr = len; + break; + case PRINT_C_LONG: + longptr = va_arg(args, long int *); + *longptr = len; + break; + case PRINT_C_LLONG: + llongptr = va_arg(args, LLONG *); + *llongptr = len; + break; + case PRINT_C_SIZE: + /* + * C99 says that with the "z" length + * modifier, "a following `n' conversion + * specifier applies to a pointer to a + * signed integer type corresponding to + * size_t argument." (7.19.6.1, 7) + */ + sizeptr = va_arg(args, SSIZE_T *); + *sizeptr = len; + break; + case PRINT_C_INTMAX: + intmaxptr = va_arg(args, INTMAX_T *); + *intmaxptr = len; + break; + case PRINT_C_PTRDIFF: + ptrdiffptr = va_arg(args, PTRDIFF_T *); + *ptrdiffptr = len; + break; + default: + intptr = va_arg(args, int *); + *intptr = len; + break; + } + break; + case '%': /* Print a "%" character verbatim. */ + OUTCHAR(str, len, size, ch); + break; + default: /* Skip other characters. */ + break; + } + ch = *format++; + state = PRINT_S_DEFAULT; + base = cflags = flags = width = 0; + precision = -1; + break; + } +out: + if (len < size) + str[len] = '\0'; + else if (size > 0) + str[size - 1] = '\0'; + + if (overflow || len >= INT_MAX) { + return -1; + } + return (int)len; +} + +static void fmtstr(char *str, size_t *len, size_t size, const char *value, + int width, int precision, int flags) +{ + int padlen, strln; /* Amount to pad. */ + int noprecision = (precision == -1); + + if (value == NULL) /* We're forgiving. */ + value = "(null)"; + + /* If a precision was specified, don't read the string past it. */ + for (strln = 0; value[strln] != '\0' && (noprecision || strln < precision); + strln++) + continue; + + if ((padlen = width - strln) < 0) + padlen = 0; + if (flags & PRINT_F_MINUS) /* Left justify. */ + padlen = -padlen; + + while (padlen > 0) { /* Leading spaces. */ + OUTCHAR(str, *len, size, ' '); + padlen--; + } + while (*value != '\0' && (noprecision || precision-- > 0)) { + OUTCHAR(str, *len, size, *value); + value++; + } + while (padlen < 0) { /* Trailing spaces. */ + OUTCHAR(str, *len, size, ' '); + padlen++; + } +} + +static void fmtint(char *str, size_t *len, size_t size, INTMAX_T value, + int base, int width, int precision, int flags) +{ + UINTMAX_T uvalue; + char iconvert[MAX_CONVERT_LENGTH]; + char sign = 0; + char hexprefix = 0; + int spadlen = 0; /* Amount to space pad. */ + int zpadlen = 0; /* Amount to zero pad. */ + int pos; + int separators = (flags & PRINT_F_QUOTE); + int noprecision = (precision == -1); + + if (flags & PRINT_F_UNSIGNED) + uvalue = value; + else { + uvalue = (value >= 0) ? value : -value; + if (value < 0) + sign = '-'; + else if (flags & PRINT_F_PLUS) /* Do a sign. */ + sign = '+'; + else if (flags & PRINT_F_SPACE) + sign = ' '; + } + + pos = convert(uvalue, iconvert, sizeof(iconvert), base, flags & PRINT_F_UP); + + if (flags & PRINT_F_NUM && uvalue != 0) { + /* + * C99 says: "The result is converted to an `alternative form'. + * For `o' conversion, it increases the precision, if and only + * if necessary, to force the first digit of the result to be a + * zero (if the value and precision are both 0, a single 0 is + * printed). For `x' (or `X') conversion, a nonzero result has + * `0x' (or `0X') prefixed to it." (7.19.6.1, 6) + */ + switch (base) { + case 8: + if (precision <= pos) + precision = pos + 1; + break; + case 16: + hexprefix = (flags & PRINT_F_UP) ? 'X' : 'x'; + break; + } + } + + if (separators) /* Get the number of group separators we'll print. */ + separators = getnumsep(pos); + + zpadlen = precision - pos - separators; + spadlen = width /* Minimum field width. */ + - separators /* Number of separators. */ + - MAX(precision, pos) /* Number of integer digits. */ + - ((sign != 0) ? 1 : 0) /* Will we print a sign? */ + - ((hexprefix != 0) ? 2 : 0); /* Will we print a prefix? */ + + if (zpadlen < 0) + zpadlen = 0; + if (spadlen < 0) + spadlen = 0; + + /* + * C99 says: "If the `0' and `-' flags both appear, the `0' flag is + * ignored. For `d', `i', `o', `u', `x', and `X' conversions, if a + * precision is specified, the `0' flag is ignored." (7.19.6.1, 6) + */ + if (flags & PRINT_F_MINUS) /* Left justify. */ + spadlen = -spadlen; + else if (flags & PRINT_F_ZERO && noprecision) { + zpadlen += spadlen; + spadlen = 0; + } + while (spadlen > 0) { /* Leading spaces. */ + OUTCHAR(str, *len, size, ' '); + spadlen--; + } + if (sign != 0) /* Sign. */ + OUTCHAR(str, *len, size, sign); + if (hexprefix != 0) { /* A "0x" or "0X" prefix. */ + OUTCHAR(str, *len, size, '0'); + OUTCHAR(str, *len, size, hexprefix); + } + while (zpadlen > 0) { /* Leading zeros. */ + OUTCHAR(str, *len, size, '0'); + zpadlen--; + } + while (pos > 0) { /* The actual digits. */ + pos--; + OUTCHAR(str, *len, size, iconvert[pos]); + if (separators > 0 && pos > 0 && pos % 3 == 0) + printsep(str, len, size); + } + while (spadlen < 0) { /* Trailing spaces. */ + OUTCHAR(str, *len, size, ' '); + spadlen++; + } +} + +static void printsep(char *str, size_t *len, size_t size) +{ + OUTCHAR(str, *len, size, ','); +} + +static int getnumsep(int digits) +{ + int separators = (digits - ((digits % 3 == 0) ? 1 : 0)) / 3; + return separators; +} + +static int convert(UINTMAX_T value, char *buf, size_t size, int base, int caps) +{ + const char *digits = caps ? "0123456789ABCDEF" : "0123456789abcdef"; + size_t pos = 0; + + /* We return an unterminated buffer with the digits in reverse order. */ + do { + buf[pos++] = digits[value % base]; + value /= base; + } while (value != 0 && pos < size); + + return (int)pos; +} + +int vsprintf(char *buf, const char *fmt, va_list args) +{ + return vsnprintf(buf, INT_MAX, fmt, args); +} diff --git a/src/vsprintf.h b/src/vsprintf.h new file mode 100644 index 00000000..cff6c932 --- /dev/null +++ b/src/vsprintf.h @@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: MIT */ + +#ifndef VSPRINTF_H +#define VSPRINTF_H + +#include + +int vsprintf(char *buf, const char *fmt, va_list args); +int vsnprintf(char *buf, size_t size, const char *fmt, va_list args); + +#endif diff --git a/src/xnuboot.h b/src/xnuboot.h new file mode 100644 index 00000000..c304df04 --- /dev/null +++ b/src/xnuboot.h @@ -0,0 +1,35 @@ +/* SPDX-License-Identifier: MIT */ + +#ifndef XNUBOOT_H +#define XNUBOOT_H + +#define CMDLINE_LENGTH 608 + +struct boot_video { + u64 base; + u64 display; + u64 stride; + u64 width; + u64 height; + u64 depth; +}; + +struct boot_args { + u16 revision; + u16 version; + u64 virt_base; + u64 phys_base; + u64 mem_size; + u64 top_of_kernel_data; + struct boot_video video; + u32 machine_type; + void *devtree; + u32 devtree_size; + char cmdline[CMDLINE_LENGTH]; + u64 boot_flags; + u64 mem_size_actual; +}; + +extern struct boot_args cur_boot_args; + +#endif