summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorZachary Yedidia <zyedidia@gmail.com>2022-05-12 16:53:06 -0700
committerZachary Yedidia <zyedidia@gmail.com>2022-05-12 16:53:06 -0700
commit723c7189369c98bbf6f2243a3292ad05a1658f16 (patch)
treef730efbacd10e27e6028440817dcf68e78e692ec
parent357d6622fe0d993dbd67898f4e4d420c9c17f50f (diff)
Boot a basic programlink-high
-rw-r--r--kern/boot.c5
-rw-r--r--kern/interrupts-asm.s5
-rw-r--r--kern/kern.c9
-rw-r--r--kern/kern.mk16
-rw-r--r--kern/kmalloc.c9
-rw-r--r--kern/kmalloc.h1
-rw-r--r--kern/kmalloc_old.c243
-rw-r--r--kern/p-basic.c5
-rw-r--r--kern/proc.c28
-rw-r--r--kern/proc.h11
-rw-r--r--kern/syscall.c21
-rw-r--r--kern/vm.c13
-rw-r--r--kern/vm.h9
-rw-r--r--prog/basic/Makefile14
-rw-r--r--prog/basic/basic.ld20
-rw-r--r--prog/basic/basic.s6
-rw-r--r--todo.txt4
-rwxr-xr-xtools/exify/exify.sh6
18 files changed, 121 insertions, 304 deletions
diff --git a/kern/boot.c b/kern/boot.c
index f75422f..120e202 100644
--- a/kern/boot.c
+++ b/kern/boot.c
@@ -18,9 +18,6 @@ static void __attribute__((section(".text.boot"))) dmap_kernel_sections();
void __attribute__((section(".text.boot"))) cstart();
extern void __attribute__((section(".text.boot"))) _hlt();
-// 1mb
-#define SEC_SIZE (1 << 20)
-
// map va to pa in the kernel pagetable
static void vm_kernel_map(uintptr_t va, uintptr_t pa) {
pte_1mb_t* pgtbl = (pte_1mb_t*) ka2pa((uintptr_t) kernel_pagetable);
@@ -45,7 +42,7 @@ static void dmap_kernel_section(uintptr_t pa) {
// double map all kernel regions
static void dmap_kernel_sections() {
// map kernel
- for (uintptr_t pa = 0; pa < MEMSIZE_PHYSICAL; pa += SEC_SIZE) {
+ for (uintptr_t pa = 0; pa < MEMSIZE_PHYSICAL; pa += SIZE_1MB) {
dmap_kernel_section(pa);
}
// map uart, gpio, watchdog timer
diff --git a/kern/interrupts-asm.s b/kern/interrupts-asm.s
index cca05ce..ee440aa 100644
--- a/kern/interrupts-asm.s
+++ b/kern/interrupts-asm.s
@@ -29,14 +29,13 @@ asm_undef_insn:
ldr sp, =kstack
ldr pc, _vec_undef_insn
asm_software_irq:
- ldr sp, =curproc
+ ldr sp, =kstack
sub lr, lr, #4
push {lr}
stmfd sp, {r0-r14}^
+ sub sp, sp, #60
mov r0, sp
- ldr sp, =kstack
bl syscall
- ldr sp, =curproc
ldm sp, {r0-r15}^
asm_prefetch_abort:
ldr sp, =kstack
diff --git a/kern/kern.c b/kern/kern.c
index b70d9ed..bb821dc 100644
--- a/kern/kern.c
+++ b/kern/kern.c
@@ -8,6 +8,8 @@
#include "sys.h"
#include "uart.h"
#include "vm.h"
+#include "proc.h"
+#include "kmalloc.h"
void reboot() {
printf("DONE!!!\n");
@@ -35,9 +37,16 @@ void kernel_start() {
uart_init(115200);
init_printf(NULL, uart_putc);
irq_init();
+ kmalloc_init();
printf("kernel booted\n");
+ proc_t p_basic;
+ extern unsigned char prog_basic[];
+ extern size_t prog_basic_sz;
+ proc_new(&p_basic, &prog_basic[0], prog_basic_sz);
+ proc_run(&p_basic);
+
reboot();
return;
}
diff --git a/kern/kern.mk b/kern/kern.mk
index a5dd7e2..19dfa6e 100644
--- a/kern/kern.mk
+++ b/kern/kern.mk
@@ -7,18 +7,4 @@ KERN_HSRC = $(wildcard $(KERN)/*.h) $(wildcard $(PIOS)/libc/*.h)
KERN_SSRC = $(wildcard $(KERN)/*.s)
KERN_OBJ_NOSAN = $(KERN)/ksan.o $(KERN)/boot.o $(KERN)/uart.o $(KERN)/start.o $(KERN)/gpio.o $(KERN)/kmalloc.o $(PIOS)/libc/tinyprintf.o
-
-KERN_OBJ = $(KERN)/boot.o \
- $(KERN)/start.o \
- $(KERN)/uart.o \
- $(KERN)/gpio.o \
- $(KERN)/kern.o \
- $(KERN)/interrupts-asm.o \
- $(KERN)/interrupts.o \
- $(KERN)/syscall.o \
- $(KERN)/kmalloc.o \
- $(KERN)/vm.o \
- $(KERN)/proc.o \
- $(KERN)/ksan.o \
- $(PIOS)/libc/libc.o \
- $(PIOS)/libc/tinyprintf.o
+KERN_OBJ = $(KERN_CSRC:.c=.o) $(KERN_SSRC:.s=.o)
diff --git a/kern/kmalloc.c b/kern/kmalloc.c
index b6d4cf5..e57b87f 100644
--- a/kern/kmalloc.c
+++ b/kern/kmalloc.c
@@ -76,11 +76,11 @@ static free_page_t* pn_to_free(uintptr_t pn) {
extern char _kheap_start;
-// Initialize everything needed for kalloc
-void init_kalloc() {
+// Initialize everything needed for kmalloc
+void kmalloc_init() {
// Iterate through all heap memory, mark as free, and coalesce blocks
// together if possible
- uintptr_t heap_start = (uintptr_t) &_kheap_start;
+ uintptr_t heap_start = ka2pa((uintptr_t) &_kheap_start);
for (uintptr_t pa = heap_start; pa < MEMSIZE_PHYSICAL; pa += PAGESIZE) {
uintptr_t pn = pagenum(pa);
pages[pn].free = true;
@@ -97,6 +97,7 @@ void init_kalloc() {
order++;
pages[pn].order = 0;
pn = bpn;
+ continue;
}
break;
}
@@ -104,7 +105,7 @@ void init_kalloc() {
// Now we set up the free lists by looping over each block and adding it to
// the list
- uintptr_t pn = 0;
+ uintptr_t pn = pagenum(heap_start);
while (pn < pagenum(MEMSIZE_PHYSICAL)) {
phys_page_t page = pages[pn];
assert(valid(pn, page.order));
diff --git a/kern/kmalloc.h b/kern/kmalloc.h
index 82cbba0..3c4c54b 100644
--- a/kern/kmalloc.h
+++ b/kern/kmalloc.h
@@ -3,6 +3,7 @@
#include <stdint.h>
#include <string.h>
+void kmalloc_init();
void* kmalloc(size_t size);
void* kmalloc_aligned(size_t size, size_t align);
void kfree(void* p);
diff --git a/kern/kmalloc_old.c b/kern/kmalloc_old.c
deleted file mode 100644
index 6d43506..0000000
--- a/kern/kmalloc_old.c
+++ /dev/null
@@ -1,243 +0,0 @@
-#include <stdbool.h>
-#include <stdint.h>
-
-#include "kern.h"
-#include "kmalloc.h"
-
-union align {
- double d;
- void* p;
- void (*fp)(void);
-};
-
-typedef union free_hdr { /* block header */
- struct {
- union free_hdr* ptr; /* next block if on free list */
- unsigned size; /* size of this block */
- } s;
- union align x; /* force alignment of blocks */
-} free_hdr_t;
-
-extern char _kheap_start;
-uintptr_t heap_start() {
- return (uintptr_t) &_kheap_start;
-}
-
-static uintptr_t _kheap_end = (uintptr_t) &_kheap_start;
-uintptr_t heap_end() {
- return _kheap_end;
-}
-
-static inline uintptr_t align_off(uintptr_t ptr, size_t align) {
- return ((~ptr) + 1) & (align - 1);
-}
-
-static void* sbrk(size_t size, size_t align) {
- void* ret = (void*) _kheap_end;
- _kheap_end += size;
- _kheap_end += align_off(_kheap_end, __alignof__(free_hdr_t));
- return ret;
-}
-
-static free_hdr_t base; /* empty list to get started */
-static free_hdr_t* freep = NULL; /* start of free list */
-
-static void kr_free(void* ap);
-
-#define NALLOC 1024 /* minimum #units to request */
-/* morecore: ask system for more memory */
-static free_hdr_t* morecore(unsigned nu) {
- char* cp;
- free_hdr_t* up;
- if (nu < NALLOC)
- nu = NALLOC;
- cp = sbrk(nu * sizeof(free_hdr_t));
- if (cp == (char*) -1) /* no space at all */
- return NULL;
- up = (free_hdr_t*) cp;
- up->s.size = nu;
- kr_free((void*) (up + 1));
- return freep;
-}
-
-static void* kr_malloc(size_t nbytes) {
- free_hdr_t *p, *prevp;
- unsigned nunits;
- nunits = (nbytes + sizeof(free_hdr_t) - 1) / sizeof(free_hdr_t) + 1;
- if ((prevp = freep) == NULL) { /* no free list yet */
- base.s.ptr = freep = prevp = &base;
- base.s.size = 0;
- }
- for (p = prevp->s.ptr;; prevp = p, p = p->s.ptr) {
- if (p->s.size >= nunits) { /* big enough */
- if (p->s.size == nunits) /* exactly */
- prevp->s.ptr = p->s.ptr;
- else { /* allocate tail end */
- p->s.size -= nunits;
- p += p->s.size;
- p->s.size = nunits;
- }
- freep = prevp;
- return (void*) (p + 1);
- }
- if (p == freep) /* wrapped around free list */
- if ((p = morecore(nunits)) == NULL)
- return NULL; /* none left */
- }
-}
-
-/* free: put block ap in free list */
-static void kr_free(void* ap) {
- free_hdr_t *bp, *p;
- bp = (free_hdr_t*) ap - 1; /* point to block header */
- for (p = freep; !(bp > p && bp < p->s.ptr); p = p->s.ptr)
- if (p >= p->s.ptr && (bp > p || bp < p->s.ptr))
- break; /* freed block at start or end of arena */
- if (bp + bp->s.size == p->s.ptr) { /* join to upper nbr */
- bp->s.size += p->s.ptr->s.size;
- bp->s.ptr = p->s.ptr->s.ptr;
- } else
- bp->s.ptr = p->s.ptr;
- if (p + p->s.size == bp) { /* join to lower nbr */
- p->s.size += bp->s.size;
- p->s.ptr = bp->s.ptr;
- } else
- p->s.ptr = bp;
- freep = p;
-}
-
-#if (SANITIZE == 0)
-
-void* kmalloc(size_t sz) {
- return kr_malloc(sz);
-}
-
-void kfree(void* p) {
- kr_free(p);
-}
-
-#else
-
-typedef struct asan_hdr {
- size_t size;
- struct asan_hdr* next;
- struct asan_hdr* prev;
-} asan_hdr_t;
-
-static asan_hdr_t* alloc_list;
-
-static void ll_insert(asan_hdr_t* n) {
- n->next = alloc_list;
- n->prev = NULL;
- if (alloc_list)
- alloc_list->prev = n;
- alloc_list = n;
-}
-
-static void ll_remove(asan_hdr_t* n) {
- if (n->next)
- n->next->prev = n->prev;
- if (n->prev)
- n->prev->next = n->next;
- else
- alloc_list = n->next;
-}
-
-static char* blk_start(asan_hdr_t* h) {
- return (char*) h + sizeof(asan_hdr_t);
-}
-
-static char* blk_end(asan_hdr_t* h) {
- return (char*) h + sizeof(asan_hdr_t) + h->size;
-}
-
-void* kmalloc(size_t sz) {
- asan_hdr_t* h = (asan_hdr_t*) kr_malloc(sz + sizeof(asan_hdr_t));
- h->size = sz;
- ll_insert(h);
- return (void*) blk_start(h);
-}
-
-void kfree(void* p) {
- asan_hdr_t* h = (asan_hdr_t*) ((char*) p - sizeof(asan_hdr_t));
- ll_remove(h);
- kr_free(h);
-}
-
-static bool asan = false;
-
-static bool in_range(char* addr, char* start, char* end) {
- return addr >= start && addr < end;
-}
-
-static void asan_access(unsigned long addr, size_t sz, bool write) {
- if (!asan) {
- return;
- }
-
- extern char _ktext_start, _ktext_end;
- if (write && in_range((char*) addr, &_ktext_start, &_ktext_end)) {
- panic("write to code segment: 0x%lx\n", addr);
- }
- extern char _krodata_start, _krodata_end;
- if (write && in_range((char*) addr, &_krodata_start, &_krodata_end)) {
- panic("write to read-only data segment: 0x%lx\n", addr);
- }
- if (in_range((char*) addr, &_kheap_start, (char*) _kheap_end)) {
- asan_hdr_t* h = alloc_list;
- while (h) {
- if (in_range((char*) addr, blk_start(h), blk_end(h))) {
- return;
- }
- h = h->next;
- }
- panic("illegal heap memory access: 0x%lx\n", addr);
- }
-}
-
-void __asan_load1_noabort(unsigned long addr) {
- asan_access(addr, 1, false);
-}
-void __asan_load2_noabort(unsigned long addr) {
- asan_access(addr, 2, false);
-}
-void __asan_load4_noabort(unsigned long addr) {
- asan_access(addr, 4, false);
-}
-void __asan_load8_noabort(unsigned long addr) {
- asan_access(addr, 8, false);
-}
-void __asan_loadN_noabort(unsigned long addr, size_t sz) {
- asan_access(addr, sz, false);
-}
-
-void __asan_store1_noabort(unsigned long addr) {
- asan_access(addr, 1, true);
-}
-void __asan_store2_noabort(unsigned long addr) {
- asan_access(addr, 2, true);
-}
-void __asan_store4_noabort(unsigned long addr) {
- asan_access(addr, 4, true);
-}
-void __asan_store8_noabort(unsigned long addr) {
- asan_access(addr, 8, true);
-}
-void __asan_storeN_noabort(unsigned long addr, size_t sz) {
- asan_access(addr, sz, true);
-}
-
-void __asan_handle_no_return() {}
-void __asan_before_dynamic_init(const char* module_name) {}
-void __asan_after_dynamic_init() {}
-
-void asan_enable() {
- /* asan = true; */
-}
-
-#endif
-
-void* kmalloc_aligned(size_t sz, size_t align) {
- uintptr_t x = (uintptr_t) kmalloc(sz + align);
- return (void*) (x + align_off(x, align));
-}
diff --git a/kern/p-basic.c b/kern/p-basic.c
new file mode 100644
index 0000000..2c4d42e
--- /dev/null
+++ b/kern/p-basic.c
@@ -0,0 +1,5 @@
+#include <string.h>
+unsigned char prog_basic[] = {
+ 0x00, 0x00, 0xa0, 0xe3, 0x00, 0x00, 0x00, 0xef,
+};
+size_t prog_basic_sz = sizeof(prog_basic);
diff --git a/kern/proc.c b/kern/proc.c
index 1a87cde..32ea910 100644
--- a/kern/proc.c
+++ b/kern/proc.c
@@ -1,17 +1,33 @@
+#include "kern.h"
#include "proc.h"
#include "vm.h"
+#include "kmalloc.h"
static pid_t id;
proc_t procs[NPROC];
proc_t* curproc;
-void proc_new(proc_t* proc) {
- *proc = (proc_t) {
- .id = id++,
- .pt = kalloc_pt(),
- .state = PROC_RUNNABLE,
- };
+void proc_new(proc_t* proc, uint8_t* code, size_t codesz) {
+ proc->id = id++;
+ proc->pt = kalloc_pt();
+ proc->state = PROC_RUNNABLE;
+ proc->code = code;
+ proc->codesz = codesz;
+ // map kernel into pt
+ for (uintptr_t pa = 0; pa < MEMSIZE_PHYSICAL; pa += SIZE_1MB) {
+ vm_map(proc->pt, pa2ka(pa), pa, PAGE_1MB);
+ }
+ // map kernel devices
+ vm_map(proc->pt, pa2ka(0x20000000), 0x20000000, PAGE_1MB);
+ vm_map(proc->pt, pa2ka(0x20100000), 0x20100000, PAGE_1MB);
+ vm_map(proc->pt, pa2ka(0x20200000), 0x20200000, PAGE_1MB);
+ // map proc code into pt
+ void* pgs = kmalloc(proc->codesz);
+ memcpy(pgs, proc->code, proc->codesz);
+ for (size_t n = 0; n < proc->codesz; n += SIZE_4KB) {
+ vm_map(proc->pt, PROC_ENTRY + n, ka2pa((uintptr_t) pgs + n), PAGE_4KB);
+ }
proc->regs.pc = PROC_ENTRY;
}
diff --git a/kern/proc.h b/kern/proc.h
index f06a789..b3ca923 100644
--- a/kern/proc.h
+++ b/kern/proc.h
@@ -1,14 +1,13 @@
#pragma once
#include <stdint.h>
+#include <string.h>
#include "vm.h"
#define PROC_ENTRY 0x8000
#define NPROC 16
-typedef uint32_t pid_t;
-
typedef struct {
uint32_t r0;
uint32_t r1;
@@ -36,8 +35,14 @@ typedef enum {
typedef struct {
regs_t regs;
- pid_t id;
+ uint32_t id;
pagetable_t* pt;
+ uint8_t* code;
+ size_t codesz;
+
proc_state_t state;
} proc_t;
+
+void proc_new(proc_t* proc, uint8_t* code, size_t codesz);
+void proc_run(proc_t* proc);
diff --git a/kern/syscall.c b/kern/syscall.c
index 21f1474..8d9e099 100644
--- a/kern/syscall.c
+++ b/kern/syscall.c
@@ -3,24 +3,7 @@
#include "kern.h"
#include "syscall.h"
#include "vm.h"
-
-typedef struct {
- uint32_t r0;
- uint32_t r1;
- uint32_t r2;
- uint32_t r3;
- uint32_t r4;
- uint32_t r5;
- uint32_t r6;
- uint32_t r7;
- uint32_t r8;
- uint32_t r9;
- uint32_t r10;
- uint32_t r11;
- uint32_t r12;
- uint32_t r13;
- uint32_t r14;
-} user_regs_t;
+#include "proc.h"
unsigned syscall_alloc_page(uint32_t page_addr, uint32_t page_size) {
assert(page_addr == SYSCALL_ARG_ANY_PAGE);
@@ -42,7 +25,7 @@ unsigned syscall_vm_map(uint32_t va,
return -1;
}
-void syscall(user_regs_t* regs) {
+void syscall(regs_t* regs) {
unsigned sysno = regs->r0;
switch (sysno) {
case SYSCALL_EXIT:
diff --git a/kern/vm.c b/kern/vm.c
index 50e98f6..30886d6 100644
--- a/kern/vm.c
+++ b/kern/vm.c
@@ -6,14 +6,16 @@
pagetable_t* kalloc_pt() {
pagetable_t* l1pt = (pagetable_t*) kmalloc(sizeof(pagetable_t));
+ assert(l1pt);
memset(l1pt, 0, sizeof(pagetable_t));
return l1pt;
}
static void init_second_level(pde_t* pde) {
pte_small_t* pgtbl = kmalloc(256 * sizeof(pte_small_t));
+ assert(pgtbl);
memset(pgtbl, 0, 256 * sizeof(pte_small_t));
- pde->addr = (uintptr_t) pgtbl >> 10;
+ pde->addr = ka2pa((uintptr_t) pgtbl) >> 10;
pde->tag = 0b01;
}
@@ -21,6 +23,9 @@ void vm_map(pagetable_t* pt, uintptr_t va, uintptr_t pa, pg_typ_t typ) {
unsigned idx = va >> 20;
l1pte_t* l1pte = &pt->entries[idx];
+ // TODO: allow remapping
+ assert(l1pte->pde.tag == 0);
+
switch (typ) {
case PAGE_UNMAPPED:
l1pte->pde.tag = 0b00;
@@ -33,7 +38,7 @@ void vm_map(pagetable_t* pt, uintptr_t va, uintptr_t pa, pg_typ_t typ) {
if (l1pte->pde.tag == 0b00) {
init_second_level(&l1pte->pde);
}
- pte_small_t* l2pt = (pte_small_t*) (l1pte->pde.addr << 10);
+ pte_small_t* l2pt = (pte_small_t*) pa2ka((uintptr_t) l1pte->pde.addr << 10);
pte_small_t* l2pte = &l2pt[bits_get(va, 12, 19)];
l2pte->addr = pa >> 12;
l2pte->ap = AP_NO_ACCESS;
@@ -49,5 +54,7 @@ void vm_unmap(pagetable_t* pt, uintptr_t va) {
}
void vm_set_pt(pagetable_t* pt) {
- sys_set_tlb_base((uintptr_t) pt);
+ sys_set_tlb_base(ka2pa((uintptr_t) pt));
+ sys_invalidate_tlb();
+ sys_clean_and_invalidate_cache();
}
diff --git a/kern/vm.h b/kern/vm.h
index f78293f..746ddfb 100644
--- a/kern/vm.h
+++ b/kern/vm.h
@@ -2,8 +2,8 @@
#include <stdint.h>
-#define pa2ka(pa) ((pa | (1UL << 31)))
-#define ka2pa(ka) ((ka & ~(1UL << 31)))
+#define pa2ka(pa) (((pa) | (1UL << 31)))
+#define ka2pa(ka) (((ka) & ~(1UL << 31)))
typedef struct {
unsigned tag : 2;
@@ -92,6 +92,11 @@ enum {
DOM_MANAGER = 0b11, // TLB access bits are ignored
};
+#define SIZE_4KB (1 << 12)
+#define SIZE_16KB (1 << 14)
+#define SIZE_1MB (1 << 20)
+#define SIZE_16MB (1 << 24)
+
typedef enum {
PAGE_UNMAPPED,
PAGE_4KB,
diff --git a/prog/basic/Makefile b/prog/basic/Makefile
new file mode 100644
index 0000000..f87fcbd
--- /dev/null
+++ b/prog/basic/Makefile
@@ -0,0 +1,14 @@
+include ../../defs.mk
+
+PROG = basic
+MEMMAP = $(PROG).ld
+
+$(PROG).elf: $(MEMMAP) $(PROG).o
+ $(LD) $(LDFLAGS) $(filter-out $<,$^) $(LDLIBS) -o $@
+
+$(PIOS)/kern/p-$(PROG).c: $(PROG).bin
+ $(PIOS)/tools/exify/exify.sh $< > $@ $(PROG)
+
+exostall: $(PIOS)/kern/p-$(PROG).c
+
+.PHONY: exostall
diff --git a/prog/basic/basic.ld b/prog/basic/basic.ld
new file mode 100644
index 0000000..9762c82
--- /dev/null
+++ b/prog/basic/basic.ld
@@ -0,0 +1,20 @@
+SECTIONS
+{
+ .text 0x8000 : {
+ KEEP(*(.text.boot))
+ *(.text*)
+ . = ALIGN(8);
+ }
+ .rodata : { *(.rodata*) }
+ .data : {
+ *(.data*)
+ . = ALIGN(4);
+ }
+ .bss : {
+ . = ALIGN(4);
+ *(.bss*)
+ *(COMMON)
+ . = ALIGN(8);
+ . = ALIGN(8);
+ }
+}
diff --git a/prog/basic/basic.s b/prog/basic/basic.s
new file mode 100644
index 0000000..513b22e
--- /dev/null
+++ b/prog/basic/basic.s
@@ -0,0 +1,6 @@
+.section ".text.boot"
+
+.globl start
+_start:
+ mov r0, #0
+ swi 0
diff --git a/todo.txt b/todo.txt
index 72901d5..e68cffb 100644
--- a/todo.txt
+++ b/todo.txt
@@ -6,3 +6,7 @@ riscv/portable
gpu
https://www.valvers.com/open-software/raspberry-pi/bare-metal-programming-in-c-part-4/
+
+exo:
+- asan
+- user mode
diff --git a/tools/exify/exify.sh b/tools/exify/exify.sh
index ee8b204..830abab 100755
--- a/tools/exify/exify.sh
+++ b/tools/exify/exify.sh
@@ -1,5 +1,7 @@
#!/bin/bash
-echo "uint8_t prog[] = {"
-hexdump -e '"\t" 8/1 "0x%02x, " "\n"' hello.bin
+echo "#include <string.h>"
+echo "unsigned char prog_$2[] = {"
+hexdump -e '"\t" 8/1 "0x%02x, " "\n"' $1
echo "};"
+echo "size_t prog_$2_sz = sizeof(prog_$2);"
generated by cgit on debian on lair
contact matthew@masot.net with questions or feedback