#!/usr/bin/env python3 import sys from subx import clean, classify, parse_segment, parse_instr """ Takes a packed & surveyed hex stream (with section headers) and packs it into a simulatable ELF file that can be run as a kernel: qemu-system-riscv32 -nographic -machine sifive_u -nographic -kernel out.elf sifive-u has lots of memory mapped peripherals, see https://github.com/qemu/qemu/blob/master/hw/riscv/sifive_u.c#L66-L83 Among them are two memory-mapped virtual UARTs at 0x10010000 and 0x10011000. Writing to UART0+0x0 writes to qemu's output TTY, and reading from UART0+0x4 reads from it. The MSB in UART0+0x4 is set if there is nothing to read. See full documentation here: https://sifive.cdn.prismic.io/sifive/4d063bf8-3ae6-4db6-9843-ee9076ebadf7_fe310-g000.pdf """ segments = [] segment = None for line in sys.stdin: line = clean(line) type = classify(line) if type == 'segment': (name, addr) = parse_segment(line) segment = { 'name': name, 'addr': addr, 'content': [] } segments.append(segment) elif type == 'instr': if segment == None: raise ValueError("label or code outside of segment!") instr = parse_instr(line) segment['content'] += instr else: raise ValueError("elf input should contain only segments and instructions!") entrypoint = 0x80000000 cursor = 0 def w(b): global cursor cursor += len(b) sys.stdout.buffer.write(b) def wi(num, size=2): global cursor cursor += size sys.stdout.buffer.write(num.to_bytes(size, byteorder='little')) def padto(offset): missing = offset - cursor if missing < 0: raise ValueError("already past offset!") w(b'\x00' * missing) def write_elf_header(): w(b"\x7fELF") # ELF magic wi(0x010101, 4) # 32bit, little endian wi(0, 8) # reserved wi(0x02) # e_type wi(0xf3) # e_machine wi(0x01, 4) # e_version wi(entrypoint, 4) # e_entry wi(0x34, 4) # e_phoff (Program Header offset) wi(0x00, 4) # e_shoff (Section Header offset, unused) wi(0x0004, 4) # e_flags wi(0x34) # e_ehsize wi(0x20) # e_phentsize wi(len(segments)) # e_phnum wi(0x28) # e_shentsize wi(0x0) # e_shnum wi(0x0) # e_shstrndx def write_program_header(segment): align = 0x1000 offset = (1 + cursor // align) * align start = segment['addr'] size = len(segment['content']) segment['offset'] = offset if offset % align != start % align: print("{:02x} {:02x}".format(offset, offset % align)) print("{:02x} {:02x}".format(start, start % align)) raise ValueError("improper alignment") wi(0x1, 4) # p_type wi(offset, 4) # p_offset wi(start, 4) # p_vaddr wi(start, 4) # p_paddr wi(size, 4) # p_filesz wi(size, 4) # p_memsz wi(0x5, 4) # p_flags, 0x5=rx, 0x6=rw wi(align, 4) # p_align write_elf_header() for seg in segments: write_program_header(seg) for seg in segments: padto(seg['offset']) for part in seg['content']: wi(part[0], 1)