From 8af9f1eb9514a55630be445fcab4e84b36fde9f6 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Wed, 13 Jan 2021 03:22:11 +0900 Subject: [PATCH] Initial commit Signed-off-by: Hector Martin --- .clang-format | 7 + .editorconfig | 14 + .gitignore | 2 + .gitmodules | 3 + LICENSE | 20 ++ Makefile | 66 ++++ README.md | 16 + artwork | 1 + data/bootlogo_128.png | 1 + data/bootlogo_256.png | 1 + m1n1.ld | 185 +++++++++++ src/fb.c | 62 ++++ src/fb.h | 17 + src/main.c | 25 ++ src/start.S | 61 ++++ src/startup.c | 77 +++++ src/string.c | 86 +++++ src/string.h | 15 + src/types.h | 49 +++ src/uart.c | 63 ++++ src/uart.h | 15 + src/utils.c | 80 +++++ src/utils.h | 167 ++++++++++ src/utils_asm.S | 72 +++++ src/vsprintf.c | 708 ++++++++++++++++++++++++++++++++++++++++++ src/vsprintf.h | 11 + src/xnuboot.h | 35 +++ 27 files changed, 1859 insertions(+) create mode 100644 .clang-format create mode 100644 .editorconfig create mode 100644 .gitignore create mode 100644 .gitmodules create mode 100644 LICENSE create mode 100644 Makefile create mode 100644 README.md create mode 160000 artwork create mode 120000 data/bootlogo_128.png create mode 120000 data/bootlogo_256.png create mode 100644 m1n1.ld create mode 100644 src/fb.c create mode 100644 src/fb.h create mode 100644 src/main.c create mode 100644 src/start.S create mode 100644 src/startup.c create mode 100644 src/string.c create mode 100644 src/string.h create mode 100644 src/types.h create mode 100644 src/uart.c create mode 100644 src/uart.h create mode 100644 src/utils.c create mode 100644 src/utils.h create mode 100644 src/utils_asm.S create mode 100644 src/vsprintf.c create mode 100644 src/vsprintf.h create mode 100644 src/xnuboot.h 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