From 9d86813d8cc74dacb0e373761da9369d3f3a2acf Mon Sep 17 00:00:00 2001 From: Aaron Lindsay Date: Thu, 4 Oct 2012 00:26:35 -0400 Subject: [PATCH] initcalls: Add initial implementation Create simple serial subsystem which makes use of initcalls, and convert existing serial drivers to its use. --- drivers/kernel.mk | 5 ++- drivers/pi_mini_uart.c | 20 ++++++++- drivers/pl011.c | 16 +++++++- drivers/serial.c | 40 ++++++++++++++++++ include/console.h | 2 +- include/drivers/serial.h | 27 ++++++++++++ include/init.h | 34 +++++++++++++++ include/print.h | 4 +- kernel/console.c | 8 ++-- kernel/init.c | 37 +++++++++++++++++ kernel/kernel.mk | 1 + kernel/print.c | 19 +++++---- kernel/start_kernel.c | 89 +++++----------------------------------- link.ld | 19 ++++++--- 14 files changed, 219 insertions(+), 102 deletions(-) create mode 100644 drivers/serial.c create mode 100644 include/drivers/serial.h create mode 100644 include/init.h create mode 100644 kernel/init.c diff --git a/drivers/kernel.mk b/drivers/kernel.mk index f57b01f..dc5ca10 100644 --- a/drivers/kernel.mk +++ b/drivers/kernel.mk @@ -3,6 +3,9 @@ SUBDIRS := include $(BASEDIR)/header.mk +OBJS_$(d) := \ + $(d)/serial.o + OBJS_$(d)_$(CONFIG_VEXPRESS_A9) := \ $(d)/pl011.o \ $(d)/pl111.o @@ -15,6 +18,6 @@ OBJS_$(d)_$(CONFIG_RPI) := \ $(d)/bcm2835_mailbox.o \ $(d)/bcm2835_videocore.o -KOBJS += $(OBJS_$(d)_y) +KOBJS += $(OBJS_$(d)) $(OBJS_$(d)_y) include $(BASEDIR)/footer.mk diff --git a/drivers/pi_mini_uart.c b/drivers/pi_mini_uart.c index d22ff7a..5801d07 100644 --- a/drivers/pi_mini_uart.c +++ b/drivers/pi_mini_uart.c @@ -18,8 +18,11 @@ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +#include #include +#include + #define VC_MMU_IO_OFFSET(addr) (addr - 0x7E000000 + 0x20000000) /* Auxiliary IO registers (includes both mini-UART and SPI) */ @@ -57,7 +60,7 @@ #define GPPUD VC_MMU_IO_OFFSET(0x7E200094) #define GPPUDCLK0 VC_MMU_IO_OFFSET(0x7E200098) -void mini_uart_init() { +void mini_uart_device_init() { uint32 aux_enables; uint32 gpfsel1; unsigned int i; @@ -96,10 +99,23 @@ void mini_uart_init() { *(uint32 *)AUX_MU_CNTL_REG = 2; //enable TX } -void mini_uart_putc(char c) { +int mini_uart_putc(char c) { /* Wait until the serial buffer is empty */ while (!(*(volatile uint32*)AUX_MU_LSR_REG & MINI_UART_TX_EMPTY)); /* When it's empty, write our character */ *(volatile uint32*)AUX_MU_IO_REG = c; + + return 0; } + +struct serial_dev mini_uart_dev = { + .putc = &mini_uart_putc +}; + +void mini_uart_init() { + mini_uart_device_init(); + serial_register_device(&mini_uart_dev); +} + +early_initcall(mini_uart_init); diff --git a/drivers/pl011.c b/drivers/pl011.c index 41f61e9..122f252 100644 --- a/drivers/pl011.c +++ b/drivers/pl011.c @@ -18,17 +18,31 @@ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +#include +#include #include #define PL011_SERIAL_BASE 0x10009000 #define PL011_SERIAL_FLAG_REGISTER 0x18 #define PL011_SERIAL_BUFFER_FULL (1 << 5) -void pl011_putc(char c) +int pl011_putc(char c) { /* Wait until the serial buffer is empty */ while (*(volatile uint32*)(PL011_SERIAL_BASE + PL011_SERIAL_FLAG_REGISTER) & (PL011_SERIAL_BUFFER_FULL)); /* When it's empty, put our character at the base */ *(volatile uint32*)PL011_SERIAL_BASE = c; + + return 0; } + +struct serial_dev pl011_dev = { + .putc = &pl011_putc +}; + +void pl011_init() { + serial_register_device(&pl011_dev); +} + +early_initcall(pl011_init); diff --git a/drivers/serial.c b/drivers/serial.c new file mode 100644 index 0000000..c8c5d43 --- /dev/null +++ b/drivers/serial.c @@ -0,0 +1,40 @@ +/* + Copyright (C) 2012, Aaron Lindsay + + This file is part of Aedrix. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include +#include + +struct serial_dev *first_serial_dev; + +int serial_register_device(struct serial_dev *sdev) { + if (!first_serial_dev) + first_serial_dev = sdev; + return 0; +} + +struct serial_dev *serial_first_device() { + return first_serial_dev; +} + +void serial_init() { + first_serial_dev = 0; +} + +driversubsys_initcall(serial_init); diff --git a/include/console.h b/include/console.h index 6d8179d..d5bdd59 100644 --- a/include/console.h +++ b/include/console.h @@ -22,6 +22,6 @@ #define CONSOLE_H void console_init(struct fb *f); -void console_putc(char c); +int console_putc(char c); #endif /* CONSOLE_H */ diff --git a/include/drivers/serial.h b/include/drivers/serial.h new file mode 100644 index 0000000..555a17a --- /dev/null +++ b/include/drivers/serial.h @@ -0,0 +1,27 @@ +/* + Copyright (C) 2012, Aaron Lindsay + + This file is part of Aedrix. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +struct serial_dev { + //TODO add more functions and attributes here + int (*putc)(char); +}; + +int serial_register_device(struct serial_dev *sdev); +struct serial_dev *serial_first_device(); diff --git a/include/init.h b/include/init.h new file mode 100644 index 0000000..a986397 --- /dev/null +++ b/include/init.h @@ -0,0 +1,34 @@ +/* + Copyright (C) 2012, Aaron Lindsay + + This file is part of Aedrix. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +void init_earlyinitcalls(); +void init_initcalls(); + +struct initcall_func { + void (*fptr)(); +}; + +#define _initcall(level_name, level, func) struct initcall_func func##_##level##_initcall \ + __attribute__ ((section ("." level_name "initcalls"))) = { func } + +/* The actual calls to be used externally */ +#define early_initcall(func) _initcall("early", 0, func) +#define driversubsys_initcall(func) _initcall("driversubsys", 1, func) +#define device_initcall(func) _initcall("device", 2, func) diff --git a/include/print.h b/include/print.h index 5d2efe0..1823ab2 100644 --- a/include/print.h +++ b/include/print.h @@ -21,8 +21,8 @@ #ifndef PRINT_H #define PRINT_H -void print_init(void (*putc)(char)); +void print_init(int (*putc)(char)); int print(char *fmt, ...); -int print_func(void (putcf)(char), char *fmt, ...); +int print_func(int (putcf)(char), char *fmt, ...); #endif /* PRINT_H */ diff --git a/kernel/console.c b/kernel/console.c index a3a69d9..5532dac 100644 --- a/kernel/console.c +++ b/kernel/console.c @@ -123,17 +123,17 @@ void console_newline() { } } -void console_putc(char c) { +int console_putc(char c) { char *console_buffer_end = console_buffer + chars_per_line * lines; char *write_char_at; switch (c) { case '\n': console_newline(); - return; + return 0; case '\r': curr_char = 0; - return; + return 0; default: break; } @@ -148,4 +148,6 @@ void console_putc(char c) { *write_char_at = c; console_char_to_fb(c, curr_char++, curr_line); + + return 0; } diff --git a/kernel/init.c b/kernel/init.c new file mode 100644 index 0000000..f2c0396 --- /dev/null +++ b/kernel/init.c @@ -0,0 +1,37 @@ +/* + Copyright (C) 2012, Aaron Lindsay + + This file is part of Aedrix. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include +#include + +extern uint32 early_initcalls_start, early_initcalls_end; +extern uint32 initcalls_start, initcalls_end; + +void init_earlyinitcalls() { + void (**initcall)(); + for (initcall = (void (**)()) &early_initcalls_start; initcall < (void (**)())&early_initcalls_end; initcall++) + (*initcall)(); +} + +void init_initcalls() { + void (**initcall)(); + for (initcall = (void (**)()) &initcalls_start; initcall < (void (**)())&initcalls_end; initcall++) + (*initcall)(); +} diff --git a/kernel/kernel.mk b/kernel/kernel.mk index 20aec5c..51a96a5 100644 --- a/kernel/kernel.mk +++ b/kernel/kernel.mk @@ -7,6 +7,7 @@ OBJS_$(d) := $(d)/atags.o \ $(d)/console.o \ $(d)/font.o \ $(d)/framebuffer.o \ + $(d)/init.o \ $(d)/kmalloc.o \ $(d)/list.o \ $(d)/math.o \ diff --git a/kernel/print.c b/kernel/print.c index 630750e..94b8df8 100644 --- a/kernel/print.c +++ b/kernel/print.c @@ -23,22 +23,23 @@ /* This function exists solely so crashes don't happen if putc() gets * called before it is initialized. */ -void putc_initial(char c) { +int putc_initial(char c) { (void)c; + return 0; } -void (*print_putc)(char) = &putc_initial; +int (*print_putc)(char) = &putc_initial; -void print_init(void (*putcfn)(char)) { +void print_init(int (*putcfn)(char)) { if (putcfn) print_putc = putcfn; } -void puts(void (*putc)(char), const char *s) +void puts(int (*putc)(char), const char *s) { while (*s) putc (*s++); } -void puti(void (*putc)(char), int i) +void puti(int (*putc)(char), int i) { unsigned int left; char buf[1 << (sizeof(int)*8) / 10]; @@ -64,7 +65,7 @@ void puti(void (*putc)(char), int i) putc(*p); } -void putx(void (*putc)(char), unsigned int i) { +void putx(int (*putc)(char), unsigned int i) { int j; puts(putc, "0x"); @@ -78,7 +79,7 @@ void putx(void (*putc)(char), unsigned int i) { } } -void putb(void (*putc)(char), unsigned int i) { +void putb(int (*putc)(char), unsigned int i) { int j; puts(putc, "0b"); @@ -88,7 +89,7 @@ void putb(void (*putc)(char), unsigned int i) { } } -int _print(void (*putc)(char), char *fmt, va_list arg) { +int _print(int (*putc)(char), char *fmt, va_list arg) { char *c; for (c = fmt; *c; c++) { if (*c == '%') { @@ -138,7 +139,7 @@ int print(char *fmt, ...) { } -int print_func(void (*putc)(char), char *fmt, ...) { +int print_func(int (*putc)(char), char *fmt, ...) { int ret; va_list arg; diff --git a/kernel/start_kernel.c b/kernel/start_kernel.c index 1c48d4b..2c34826 100644 --- a/kernel/start_kernel.c +++ b/kernel/start_kernel.c @@ -19,6 +19,7 @@ */ #include +#include #include #include #include @@ -26,13 +27,13 @@ #include #include +#include + #ifdef CONFIG_VEXPRESS_A9 -#include #include #endif #ifdef CONFIG_RPI -#include #include #endif @@ -56,74 +57,11 @@ void video_init(void) { #endif } -void test_mm() { - struct page *p, *q; +void serial_console_init() { + struct serial_dev *sdev = serial_first_device(); - print("\ntest_mm():\n"); - - p = mm_get_free_pages(0); - if (p) - print("%x, %x\n", p, p->address); - else - print("Error: failed to allocate memory for p\n"); - - q = mm_get_free_pages(4); - if (q) - print("%x, %x\n", q, q->address); - else - print("Error: failed to allocate memory for q\n"); - - mm_put_free_pages(p); - mm_put_free_pages(q); - - q = mm_get_free_pages(1); - if (q) - print("%x, %x\n", q, q->address); - else - print("Error: failed to allocate memory for q\n"); - - p = mm_get_free_pages(0); - if (p) - print("%x, %x\n", p, p->address); - else - print("Error: failed to allocate memory for p\n"); - mm_put_free_pages(p); - mm_put_free_pages(q); - -} - -void test_kmalloc() { - void *a, *b, *c, *d; - - print("\ntest_kmalloc():\n"); - - a = kmalloc(4); - print("a: %x\n", a); - b = kmalloc(13); - print("b: %x\n", b); - c = kmalloc(4); - print("c: %x\n", c); - d = kmalloc(25); - print("d: %x\n", d); - - kfree(c); - kfree(b); - kfree(a); - kfree(d); - - a = kmalloc(13); - print("a: %x\n", a); - b = kmalloc(4); - print("b: %x\n", b); - c = kmalloc(25); - print("c: %x\n", c); - d = kmalloc(7); - print("d: %x\n", d); -} - -void test_memory() { - test_mm(); - test_kmalloc(); + if (sdev) + print_init(sdev->putc); } void kmalloc_init(); @@ -135,14 +73,9 @@ int main(void) { //setup MMU mmu_reinit(); - //initialize the serial console -#ifdef CONFIG_VEXPRESS_A9 - print_init(&pl011_putc); -#endif -#ifdef CONFIG_RPI - mini_uart_init(); - print_init(&mini_uart_putc); -#endif + init_earlyinitcalls(); + + serial_console_init(); //setup memory mm_init(); @@ -159,7 +92,7 @@ int main(void) { declare_memory_region(lower, upper); } while (!atags_next_mem_region(&atags)); - test_memory(); + init_initcalls(); video_init(); console_init(&myfb); diff --git a/link.ld b/link.ld index 05dea3e..42f3f20 100644 --- a/link.ld +++ b/link.ld @@ -2,9 +2,18 @@ ENTRY (start) SECTIONS { - . = 0x80100000; - .text : { *(.text*) *(.rodata*) } - .data : { *(.data*) } - .bss : { *(.bss*) *(COMMON*) } - kernel_end = .; + . = 0x80100000; + .text : { *(.text*) *(.rodata*) } + .init : { + early_initcalls_start = .; + *(.earlyinitcalls*) + early_initcalls_end = .; + initcalls_start = .; + *(.driversubsysinitcalls*) + *(.deviceinitcalls*) + initcalls_end = .; + } + .data : { *(.data*) } + .bss : { *(.bss*) *(COMMON*) } + kernel_end = .; }