From 561535234d63d01560a10ce272a33ba894a5e453 Mon Sep 17 00:00:00 2001 From: Bas Wijnen Date: Mon, 27 Jul 2009 20:03:58 +0200 Subject: [PATCH] make keyboard work a bit --- .gitignore | 6 +- Makefile | 6 +- README.build | 37 +++++++++ boot-programs/devices.hhp | 137 +++++++++++++++++++++++++++++++ boot-programs/gpio.ccp | 51 ++++++------ iris.h | 166 ++++++++++++++++++++++++++++++++------ mips/Makefile.arch | 10 +-- report/kernel.tex | 35 +++++++- report/making-of.tex | 66 +++++++++++++++ 9 files changed, 454 insertions(+), 60 deletions(-) create mode 100644 README.build diff --git a/.gitignore b/.gitignore index aa364a8..0b912a3 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,6 @@ -kernel -kernel.raw -kernel.raw.gz +iris +iris.raw +iris.raw.gz uimage *.o *.cc diff --git a/Makefile b/Makefile index d4b33b5..ec04dd9 100644 --- a/Makefile +++ b/Makefile @@ -23,8 +23,8 @@ LD = $(CROSS)ld OBJCOPY = $(CROSS)objcopy headers = kernel.hh iris.h $(arch_headers) -kernel_sources = panic.cc data.cc alloc.cc memory.cc invoke.cc schedule.cc $(arch_kernel_sources) -BUILT_SOURCES = $(kernel_sources) $(boot_sources) +iris_sources = panic.cc data.cc alloc.cc memory.cc invoke.cc schedule.cc $(arch_iris_sources) +BUILT_SOURCES = $(iris_sources) $(boot_sources) # Include arch-specific rules. include Makefile.arch @@ -47,6 +47,6 @@ PYPP = /usr/bin/pypp #$(OBJCOPY) -S $(OBJCOPYFLAGS) $@ clean: - rm -f *.o boot-programs/*.o kernel kernel.raw kernel.raw.gz $(BUILT_SOURCES) $(ARCH_CLEAN_FILES) + rm -f *.o boot-programs/*.o $(BUILT_SOURCES) $(ARCH_CLEAN_FILES) .PHONY: clean diff --git a/README.build b/README.build new file mode 100644 index 0000000..24ec92d --- /dev/null +++ b/README.build @@ -0,0 +1,37 @@ +Building Iris. + +For building, you will need: +- A compiler to create mips binaries. Unless you run on a mips platform, which + is unlikely, that means a cross-compiler. +- The pythonic preprocessor, pypp. +- Python +- mkimage, from the Debian package uboot-mkimage. + +The last two can simply be installed using your favorite package manager. For +the cross compiler, please follow the instructions from +report/cross-compiler.tex. + +Pypp can be downloaded using +svn co http://a82-93-13-222.adsl.xs4all.nl/svn/trunk/pypp + +To build, it needs libshevek, which can be downloaded with +svn co http://a82-93-13-222.adsl.xs4all.nl/svn/trunk/libshevek + +For both of them, dpkg-buildpackage -uc -us in their directory creates a Debian +package which can be installed. Install libshevek*deb before building pypp. +If you are not using Debian, use +autoreconf -f -i -s +./configure --prefix=/usr +make +make install + +When all is installed, "make" should be enough to create "uimage". To use it, +make the first partition of an SD card (it really must be SD; MMC does not +work) smaller than 32 MB; I use 16 MB myself. Format it as FAT and copy uimage +to it. Then insert it in the Trendtac (it fits upside down) and boot with +Fn+left Ctrl+left Shift pressed. When caps and scroll lock are flashing, Iris +is booting (the flashing lights are from uboot, not from Iris, but they +indicate that the SD image is used). Then you can release the keys. + +If anything doesn't work, or you have other comments, please send a message to +Bas Wijnen . Thanks. diff --git a/boot-programs/devices.hhp b/boot-programs/devices.hhp index ca40ef5..dcf4a98 100644 --- a/boot-programs/devices.hhp +++ b/boot-programs/devices.hhp @@ -19,9 +19,146 @@ #ifndef __IRIS_DEVICES_HH #define __IRIS_DEVICES_HH +#include "iris.h" + +// This shouldn't really be here. But where should it be? +// Requests made by initial threads to tell init about themselves. enum init_requests: INIT_SET_GPIO_0 INIT_SET_GPIO_1 INIT_SET_LCD + +// Keyboard interface. +// Startup: kbd_set_cb +// Set the event callback. Currently pressed keys emit a key press event to the new callback immediately, plus a ~0 to signal the end of such events. +static __inline__ void keyboard_set_cb (Capability kbd_set_cb, Capability cb): + invoke_10 (kbd_set_cb, cb) +// At event: the callback is called with a keycode. One bit defines if it's a press or release event. +#define KEYBOARD_RELEASE (1 << 31) + + + +// Display interface. +// Startup: disp_set_eof_cb, disp_create_fb, disp_use_fb, disp_info + +// Register an end-of-frame callback. +// At end of frame, the callback is invoked and forgotten. It must be reregistered to keep a stream of events. +static __inline__ void display_set_eof_cb (Capability disp_set_eof_cb, Capability cb): + invoke_10 (disp_set_eof_cb, cb) + +// Create a framebuffer for the display. When not in use, it can be freed by the user. +// The pages must be cappages holding Page capabilities. They are filled by the display. +// The passed numbers must be 0 or match a mode that the device can use. +// The returned number is the physical address of the framebuffer. It can be used with display_use_framebuffer. +static __inline__ unsigned display_create_framebuffer (Capability disp_create_fb, Capability page0, Capability page1 = 0, Capability page2 = 0, unsigned w = 0, unsigned h = 0, unsigned mode = 0): + return call_n33 (disp_create_fb, page0, page1, page2, w, h, mode) + +// Use a framebuffer. The address must have been returned from display_create_framebuffer. +// w, h and mode must match the values given at creation time. +// unuse_cb is called the next time this operation is requested. +static __inline__ void display_use_framebuffer (Capability disp_use_fb, unsigned addr, Capability unuse_cb, unsigned w = 0, unsigned h = 0, unsigned mode = 0): + invoke_11 (disp_use_fb, unuse_cb, addr) + +// Get information about the display. +static __inline__ void display_get_info (Capability disp_info): + // TODO: Interface is to be designed. + + + +// File system interface. +// This may not be a server, so there need not be a startup phase. Instead, capabilities can implement certain interfaces: directory, file, stream, seekable file, mappable file. Normal files implement at least stream or seekable file. Directories implement directory. + +enum File_request: + FILE_INFO + FILE_CLOSE + FILE_COPY_HANDLE + DIRECTORY_GET_SIZE + DIRECTORY_GET_NAME + DIRECTORY_GET_FILE + DIRECTORY_GET_FILE_INFO + DIRECTORY_CREATE_FILE + DIRECTORY_DELETE_FILE + FILE_STREAM_READ + FILE_STREAM_WRITE + FILE_SEEKABLE_READ + FILE_SEEKABLE_WRITE + FILE_SEEKABLE_TRUNCATE + +// File interface. +// Get information about the file. +static __inline__ unsigned long long file_get_info (Capability file, unsigned type): + return call_l02 (file, FILE_INFO, type) +// Close a file. If this is a directory, it implicitly closes all files opened from it. +static __inline__ void file_close (Capability file): + invoke_01 (file, FILE_CLOSE) +// Copy a file handle. This can be useful for closing all children at once. The new handle itself is a child of the original handle. +static __inline__ Capability file_copy_handle (Capability file): + return call_c01 (file, FILE_COPY_HANDLE) + +// Directory interface. +// Get the number of entries in this directory. +static __inline__ unsigned long long directory_get_size (Capability dir): + return call_l01 (dir, DIRECTORY_GET_SIZE) +// Get the filename. The return value is the size of the string, the page is filled with the string itself. +static __inline__ unsigned directory_get_name (Capability dir, unsigned long long idx, Capability page): + return call_n13 (dir, page, DIRECTORY_GET_NAME, idx & 0xffffffff, idx >> 32) +// Get the file. +static __inline__ Capability directory_get_file (Capability dir, unsigned long long idx, Capability page): + return call_c03 (dir, DIRECTORY_GET_FILE, idx & 0xffffffff, idx >> 32) +// Get file info. This returns the same information as file_get_info, without opening the file. +static __inline__ unsigned long long directory_get_file_info (Capability dir, unsigned long long idx, unsigned type): + return call_l04 (dir, DIRECTORY_GET_FILE_INFO, idx & 0xffffffff, idx >> 32, type) +// Create a new file. After this, any index may map to a different file. +static __inline__ Capability directory_create_file (Capability dir, unsigned long long idx, Capability page): + return call_c03 (dir, DIRECTORY_CREATE_FILE, idx & 0xffffffff, idx >> 32) +// Delete a file. After this, any index may map to a different file. +static __inline__ Capability directory_delete_file (Capability dir, unsigned long long idx, Capability page): + return call_c03 (dir, DIRECTORY_DELETE_FILE, idx & 0xffffffff, idx >> 32) + +// Stream interface. +// Try to read size bytes. Returns the number of bytes successfully read. It cannot be more than PAGE_SIZE. +static __inline__ unsigned file_stream_read (Capability file, Capability page, unsigned size): + return call_n12 (file, page, FILE_STREAM_READ, size) +// Try to write size bytes. Returns the number of bytes successfully written. It cannot be more than PAGE_SIZE. +static __inline__ unsigned file_stream_write (Capability file, Capability page, unsigned size): + return call_n12 (file, page, FILE_STREAM_WRITE, size) + +// Seekable file interface. +// Try to read size bytes from position idx. Returns the number of bytes successfully read. It cannot be more than PAGE_SIZE. +static __inline__ unsigned file_seekable_read (Capability file, Capability page, unsigned long long idx, unsigned size): + return call_n14 (file, page, FILE_SEEKABLE_READ, idx & 0xffffffff, idx >> 32, size) +// Try to write size bytes at position idx; the file is extended with zeroes if the write is past the end. Returns the number of bytes successfully written. It cannot be more than PAGE_SIZE. +static __inline__ unsigned file_seekable_write (Capability file, Capability page, unsigned long long idx, unsigned size): + return call_n14 (file, page, FILE_SEEKABLE_WRITE, idx & 0xffffffff, idx >> 32, size) +// Truncate file to size idx. The file is extended with zeroes if it gets longer. +static __inline__ void file_seekable_truncate (Capability file, unsigned long long idx): + call_n03 (file, FILE_SEEKABLE_TRUNCATE, idx & 0xffffffff, idx >> 32) + +// Mappable file interface. +// TODO: to be designed. + + + +// Block device interface. +// Startup: blk_get_size, blk_get_file. + +// Get block size. +static __inline__ unsigned block_get_size (Capability blk_get_size): + return call_n00 (blk_get_size) + +// Get file capability. Returns a seekable mappable non-stream file. +static __inline__ Capability block_get_file (Capability blk_get_file): + call_c00 (blk_get_file) + + + +// TODO. +// Sound interface. +// Usb interfaces (port, device). +// Pointer interface. (Only movement; buttons follow keyboard interface.) +// Network interface. +// Camera interface. +// Terminal interfaces. + #endif diff --git a/boot-programs/gpio.ccp b/boot-programs/gpio.ccp index e6b96c7..74be5fc 100644 --- a/boot-programs/gpio.ccp +++ b/boot-programs/gpio.ccp @@ -21,7 +21,7 @@ #include "arch.hh" // Interval between polls for keyboard (when keys are pressed) and battery/power (always) events -#define ALARM_INTERVAL (HZ / 1) +#define ALARM_INTERVAL (HZ / 20) // GPIO pins for the devices (port.pin) @@ -29,7 +29,7 @@ // Cols = 3.0, 3.1, 3.2, 3.3, 3.4, 3.5, 3.6, 3.7, 3.8, 3.9, 3.10, 3.11, 3.12, 3.13, 3.14, 3.15, 3.29 // Rows = 0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7 // For some reason, it only works if the rows are input and the columns are output. -// interrupts: yes, with all columns set to output 0, the first key press can be detected as an interrupt. +// interrupts: yes, with all columns set to output 0, the first key press can be detected as an interrupt; some other events also trigger interrupts. // touchpad buttons // Left: 0.16 @@ -90,17 +90,22 @@ enum battery_type: class Keyboard: unsigned keys[GPIO_KBD_NUM_COLS] + bool scanning void parse (unsigned col, unsigned data): for unsigned row = 0; row < GPIO_KBD_NUM_ROWS; ++row: if (data ^ keys[col]) & (1 << row): unsigned code = (col << 3) | row if data & (1 << row): - code |= 0x10000 + code |= KEYBOARD_RELEASE event (KEYBOARD_EVENT, code) keys[col] = data + // If any keys are pressed, scanning is required. + if data != GPIO_KBD_ROW_MASK: + scanning = true public: + bool is_scanning (): + return scanning void scan (): - kdebug ("keyboard scan\n") // Disable interrupts during scan. GPIO_GPIER (GPIO_KBD_ROW_PORT) &= ~GPIO_KBD_ROW_MASK // All columns are input. @@ -108,31 +113,34 @@ class Keyboard: int const cols[GPIO_KBD_NUM_COLS] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 29 } unsigned dir = GPIO_GPDIR (GPIO_KBD_COL_PORT) & ~GPIO_KBD_COL_MASK unsigned dat = GPIO_GPDR (GPIO_KBD_COL_PORT) & ~GPIO_KBD_COL_MASK - unsigned data + // Set scanning to false before first parse. + scanning = false for unsigned col = 0; col < GPIO_KBD_NUM_COLS; ++col: // clear pin GPIO_GPDR (GPIO_KBD_COL_PORT) = dat // output GPIO_GPDIR (GPIO_KBD_COL_PORT) = dir | (1 << cols[col]) - // Generate events of previous column. Do that now, so there is a short delay for the data to stabilize. - if col != 0: - parse (col - 1, data) - else: - // Add a short delay for stabilization. - parse (0, keys[0]) - data = GPIO_GPDR (GPIO_KBD_ROW_PORT) & GPIO_KBD_ROW_MASK + for unsigned i = 0; i < 100000; ++i: + GPIO_GPDIR (GPIO_KBD_COL_PORT) = dir | (1 << cols[col]) + unsigned data = GPIO_GPDR (GPIO_KBD_ROW_PORT) & GPIO_KBD_ROW_MASK + parse (col, data) // set pin GPIO_GPDR (GPIO_KBD_COL_PORT) = dat | (1 << cols[col]) // input GPIO_GPDIR (GPIO_KBD_COL_PORT) = dir - parse (GPIO_KBD_NUM_COLS - 1, data) // set all to 0. GPIO_GPDR (GPIO_KBD_COL_PORT) = dat // set all to output. GPIO_GPDIR (GPIO_KBD_COL_PORT) = dir | GPIO_KBD_COL_MASK - // clear pending interrupts. + // Set interrupts on change. + unsigned data = GPIO_GPDR (GPIO_KBD_ROW_PORT) for unsigned i = 0; i < 8; ++i: - GPIO_GPFR (GPIO_KBD_ROW_PORT) |= 1 << i + if data & (1 << i): + gpio_irq_fall (GPIO_KBD_ROW_PORT, i) + else: + gpio_irq_rise (GPIO_KBD_ROW_PORT, i) + // Clear pending interrupts. + GPIO_GPFR (GPIO_KBD_ROW_PORT) |= GPIO_KBD_ROW_MASK // Reenable interrupts. GPIO_GPIER (GPIO_KBD_ROW_PORT) |= GPIO_KBD_ROW_MASK Keyboard (): @@ -143,9 +151,6 @@ class Keyboard: // Set all rows to input and enable the pull-ups. GPIO_GPPUR (GPIO_KBD_ROW_PORT) |= GPIO_KBD_ROW_MASK GPIO_GPDIR (GPIO_KBD_ROW_PORT) &= ~GPIO_KBD_ROW_MASK - // Detect interrupts on falling edge. - for unsigned i = 0; i < GPIO_KBD_NUM_ROWS; ++i: - gpio_irq_fall (GPIO_KBD_ROW_PORT, i) // Initialize matrix. for unsigned i = 0; i < GPIO_KBD_NUM_COLS; ++i: keys[i] = 0xff @@ -174,6 +179,8 @@ class Touchpad: if (state ^ old_state) & (1 << GPIO_TP_RIGHT): event (TOUCHPAD_EVENT, 0x10001) old_state = state + // Ack interrupts. + GPIO_GPFR (GPIO_TP_LEFT_PORT) = (1 << GPIO_TP_LEFT) | (1 << GPIO_TP_RIGHT) Touchpad (): // Set pins to input with pull-ups. gpio_as_input (GPIO_TP_LEFT_PORT, GPIO_TP_LEFT) @@ -313,17 +320,13 @@ int main (): switch msg.protected_data: case ~0: // Alarm. - kdebug ("alarm\n") // Periodically scan several devices. - kbd.scan () + if kbd.is_scanning (): + kbd.scan () power.poll () receiver_set_alarm (__my_receiver, ALARM_INTERVAL) break case IRQ_GPIO0: - kdebug ("gpio interrupt\n") - //unsigned irq = GPIO_GPFR (0) - // Ack all. - GPIO_GPFR (0) = (1 << GPIO_TP_LEFT) | (1 << GPIO_TP_RIGHT) | GPIO_KBD_ROW_MASK // Always scan keyboard and touchpad on any interrupt. kbd.scan () tp.check_events () diff --git a/iris.h b/iris.h index 37a1d9f..8def656 100644 --- a/iris.h +++ b/iris.h @@ -117,8 +117,8 @@ static const char *exception_name[NUM_EXCEPTION_CODES] = { #define CAP_THREAD_MAKE_PRIV 6 #define CAP_THREAD_GET_TOP_MEMORY 7 #define CAP_THREAD_REGISTER_INTERRUPT 8 -#define CAP_THREAD_ALL_RIGHTS 0x0f -#define CAP_THREAD_ALL_PRIV_RIGHTS (CAP_THREAD_ALL_RIGHTS | (1 << CAP_THREAD_REGISTER_INTERRUPT) | (1 << CAP_THREAD_GET_TOP_MEMORY) | (1 << CAP_THREAD_MAKE_PRIV) | (1 << CAP_THREAD_ALLOC_PHYSICAL) | (1 << CAP_THREAD_PHYSICAL_ADDRESS)) +#define CAP_THREAD_ALL_RIGHTS 0x07 +#define CAP_THREAD_ALL_PRIV_RIGHTS (CAP_THREAD_ALL_RIGHTS | (1 << CAP_THREAD_REGISTER_INTERRUPT) | (1 << CAP_THREAD_GET_TOP_MEMORY) | (1 << CAP_THREAD_MAKE_PRIV) | (1 << CAP_THREAD_ALLOC_PHYSICAL) | (1 << CAP_THREAD_PHYSICAL_ADDRESS) | (1 << CAP_THREAD_ALLOC_RANGE)) /* These get/set_info are not arch-specific. */ #define CAP_THREAD_INFO_PC ~0 @@ -455,6 +455,36 @@ static void call_00 (Capability c) call (__my_call, &msg); } +static unsigned call_n00 (Capability c) +{ + Message msg; + msg.cap[0] = c; + msg.data[0] = 0; + msg.data[1] = 0; + msg.data[2] = 0; + msg.data[3] = 0; + msg.cap[1] = 0; + msg.cap[2] = 0; + msg.cap[3] = 0; + call (__my_call, &msg); + return msg.data[0]; +} + +static Capability call_c00 (Capability c) +{ + Message msg; + msg.cap[0] = c; + msg.data[0] = 0; + msg.data[1] = 0; + msg.data[2] = 0; + msg.data[3] = 0; + msg.cap[1] = 0; + msg.cap[2] = 0; + msg.cap[3] = 0; + call (__my_call, &msg); + return msg.cap[0]; +} + static Capability call_c01 (Capability c, unsigned d) { Message msg; @@ -470,6 +500,21 @@ static Capability call_c01 (Capability c, unsigned d) return msg.cap[0]; } +static unsigned long long call_l01 (Capability c, unsigned d) +{ + Message msg; + msg.cap[0] = c; + msg.data[0] = d; + msg.data[1] = 0; + msg.data[2] = 0; + msg.data[3] = 0; + msg.cap[1] = 0; + msg.cap[2] = 0; + msg.cap[3] = 0; + call (__my_call, &msg); + return (unsigned long long)msg.data[0] | ((unsigned long long)msg.data[1] << 32); +} + static Capability call_c02 (Capability c, unsigned d0, unsigned d1) { Message msg; @@ -485,6 +530,51 @@ static Capability call_c02 (Capability c, unsigned d0, unsigned d1) return msg.cap[0]; } +static unsigned long long call_l02 (Capability c, unsigned d0, unsigned d1) +{ + Message msg; + msg.cap[0] = c; + msg.data[0] = d0; + msg.data[1] = d1; + msg.data[2] = 0; + msg.data[3] = 0; + msg.cap[1] = 0; + msg.cap[2] = 0; + msg.cap[3] = 0; + call (__my_call, &msg); + return (unsigned long long)msg.data[0] | ((unsigned long long)msg.data[1] << 32); +} + +static Capability call_c03 (Capability c, unsigned d0, unsigned d1, unsigned d2) +{ + Message msg; + msg.cap[0] = c; + msg.data[0] = d0; + msg.data[1] = d1; + msg.data[2] = d2; + msg.data[3] = 0; + msg.cap[1] = 0; + msg.cap[2] = 0; + msg.cap[3] = 0; + call (__my_call, &msg); + return msg.cap[0]; +} + +static unsigned long long call_l04 (Capability c, unsigned d0, unsigned d1, unsigned d2, unsigned d3) +{ + Message msg; + msg.cap[0] = c; + msg.data[0] = d0; + msg.data[1] = d1; + msg.data[2] = d2; + msg.data[3] = d3; + msg.cap[1] = 0; + msg.cap[2] = 0; + msg.cap[3] = 0; + call (__my_call, &msg); + return (unsigned long long)msg.data[0] | ((unsigned long long)msg.data[1] << 32); +} + static Capability call_c12 (Capability c, Capability c1, unsigned d0, unsigned d1) { Message msg; @@ -545,6 +635,21 @@ static unsigned call_n12 (Capability c, Capability c1, unsigned d0, unsigned d1) return msg.data[0]; } +static unsigned call_n14 (Capability c, Capability c1, unsigned d0, unsigned d1, unsigned d2, unsigned d3) +{ + Message msg; + msg.cap[0] = c; + msg.cap[1] = c1; + msg.data[0] = d0; + msg.data[1] = d1; + msg.data[2] = d2; + msg.data[3] = d3; + msg.cap[2] = 0; + msg.cap[3] = 0; + call (__my_call, &msg); + return msg.data[0]; +} + static unsigned call_n02 (Capability c, unsigned d0, unsigned d1) { Message msg; @@ -560,6 +665,22 @@ static unsigned call_n02 (Capability c, unsigned d0, unsigned d1) return msg.data[0]; } +static Capability call_p02 (Capability c, unsigned d0, unsigned d1, unsigned *base_return) +{ + Message msg; + msg.cap[0] = c; + msg.data[0] = d0; + msg.data[1] = d1; + msg.data[2] = 0; + msg.data[3] = 0; + msg.cap[1] = 0; + msg.cap[2] = 0; + msg.cap[3] = 0; + call (__my_call, &msg); + *base_return = msg.data[0]; + return msg.cap[0]; +} + static unsigned call_n03 (Capability c, unsigned d0, unsigned d1, unsigned d2) { Message msg; @@ -575,6 +696,22 @@ static unsigned call_n03 (Capability c, unsigned d0, unsigned d1, unsigned d2) return msg.data[0]; } +static unsigned call_n04 (Capability c, unsigned d0, unsigned d1, unsigned d2, unsigned d3) +{ + Message msg; + msg.cap[0] = c; + msg.data[0] = d0; + msg.data[1] = d1; + msg.data[2] = d2; + msg.data[3] = d3; + msg.data[3] = 0; + msg.cap[1] = 0; + msg.cap[2] = 0; + msg.cap[3] = 0; + call (__my_call, &msg); + return msg.data[0]; +} + static unsigned call_n13 (Capability c, Capability c1, unsigned d0, unsigned d1, unsigned d2) { Message msg; @@ -590,38 +727,21 @@ static unsigned call_n13 (Capability c, Capability c1, unsigned d0, unsigned d1, return msg.data[0]; } -static unsigned call_n04 (Capability c, unsigned d0, unsigned d1, unsigned d2, unsigned d3) +static unsigned call_n33 (Capability c, Capability c1, Capability c2, Capability c3, unsigned d0, unsigned d1, unsigned d2) { Message msg; msg.cap[0] = c; + msg.cap[1] = c1; + msg.cap[2] = c2; + msg.cap[3] = c3; msg.data[0] = d0; msg.data[1] = d1; msg.data[2] = d2; - msg.data[3] = d3; msg.data[3] = 0; - msg.cap[1] = 0; - msg.cap[2] = 0; - msg.cap[3] = 0; call (__my_call, &msg); return msg.data[0]; } -static Capability call_p02 (Capability c, unsigned d0, unsigned d1, unsigned *base_return) -{ - Message msg; - msg.cap[0] = c; - msg.data[0] = d0; - msg.data[1] = d1; - msg.data[2] = 0; - msg.data[3] = 0; - msg.cap[1] = 0; - msg.cap[2] = 0; - msg.cap[3] = 0; - call (__my_call, &msg); - *base_return = msg.data[0]; - return msg.cap[0]; -} - static Capability degrade (Capability src, unsigned mask) { return call_c02 (src, CAP_DEGRADE, mask); diff --git a/mips/Makefile.arch b/mips/Makefile.arch index 05df707..6197776 100644 --- a/mips/Makefile.arch +++ b/mips/Makefile.arch @@ -24,7 +24,7 @@ OBJDUMP = $(CROSS)objdump junk = mdebug.abi32 reginfo comment pdr OBJCOPYFLAGS = $(addprefix --remove-section=.,$(junk)) -arch_kernel_sources = mips/interrupts.cc mips/arch.cc +arch_iris_sources = mips/interrupts.cc mips/arch.cc boot_sources = mips/init.cc arch_headers = mips/arch.hh mips/jz4730.hh boot_threads = init gpio lcd @@ -41,14 +41,14 @@ boot-programs/charset.data: boot-programs/charset $< > $@ # Transform ':' into ';' so vim doesn't think there are errors. -uimage: kernel.raw.gz Makefile mips/Makefile.arch - mkimage -A MIPS -O Linux -C gzip -a $(load) -e 0xa$(shell /bin/sh -c '$(OBJDUMP) -t kernel | grep __start$$ | cut -b2-8') -n "Iris" -d $< $@ | sed -e 's/:/;/g' +uimage: iris.raw.gz Makefile mips/Makefile.arch + mkimage -A MIPS -O Linux -C gzip -a $(load) -e 0xa$(shell /bin/sh -c '$(OBJDUMP) -t iris | grep __start$$ | cut -b2-8') -n "Iris" -d $< $@ | sed -e 's/:/;/g' %.o:%.S Makefile mips/Makefile.arch mips/arch.hh $(CC) $(CPPFLAGS) $(TARGET_FLAGS) -DKERNEL_STACK_SIZE=0x2000 -c $< -o $@ # entry.o must be the first file. boot.o must be the first of the init objects (which can be freed after loading). -kernel: mips/entry.o $(subst .cc,.o,$(kernel_sources)) mips/boot.o $(subst .cc,.o,$(boot_sources)) +iris: mips/entry.o $(subst .cc,.o,$(iris_sources)) mips/boot.o $(subst .cc,.o,$(boot_sources)) $(LD) --omagic -Ttext $(load) $^ -o $@ %.raw: % @@ -57,4 +57,4 @@ kernel: mips/entry.o $(subst .cc,.o,$(kernel_sources)) mips/boot.o $(subst .cc,. %.gz: % gzip < $< > $@ -ARCH_CLEAN_FILES = uimage kernel kernel.raw kernel.raw.gz $(boot_threads) mips/*.o boot-programs/charset.data +ARCH_CLEAN_FILES = uimage $(boot_threads) mips/*.o boot-programs/charset.data iris iris.raw iris.raw.gz diff --git a/report/kernel.tex b/report/kernel.tex index fb56974..200bc3d 100644 --- a/report/kernel.tex +++ b/report/kernel.tex @@ -269,16 +269,37 @@ registers and possibly other information which is different per Thread. \item Let Iris schedule the next process. This is not thread-specific. \item Get the top Memory object. This is not thread-specific. Most Threads are not allowed to perform this operation. It is given to the initial Threads. -They can pass it on to Threads that need it (mostly device drivers). +They can pass it on to Threads that need it (if any). \item In the same category, register a Receiver for an interrupt. Upon registration, the interrupt is enabled. When the interrupt arrives, the registered Receiver gets a message from Iris and the interrupt is disabled again. After the Thread has handled the interrupt, it must reregister it in order to enable it again. +\item Allocate a range of contiguous physical memory. This is only relevant +for device drivers whose device will directly access the storage, such as the +display driver. The result of this call is that the memory is counted as used +by the Thread, and it is reserved, but it is not returned. Instead, the +address of physical memory is returned, and the pages need to be retrieved with +the next operation. This capability is not present in normally created +threads. +\item Allocate a page of physical memory. This is used in combination with the +previous operation to reserve a block of physical memory, and by device drivers +to map I/O memory into their address space. There is a flag indicating whether +this memory should be freed (ranges) or not (I/O). Users of this operation are +trusted to handle it properly; no checks are done to ensure that no kernel +memory is leaked, or that the allocated memory isn't used by other threads or +the kernel. Of course, this capability is not present in normally created +threads. +\item Get the physical address of a page. Only device drivers need to know the +physical address of their pages, so this operation is not available on normal +threads. \item And similarly, allow these priviledged operations (or some of them) in an other thread. This is a property of the caller, because the target thread normally doesn't have the permission to do this (otherwise the call would not -be needed). The result of this operation is a new Thread capability with all specified rights set. Normally this is inserted in a priviledged process's address space during setup, before it is run (instead of the capability which is obtained during Thread creation). +be needed). The result of this operation is a new Thread capability with all +specified rights set. Normally this is inserted in a priviledged process's +address space during setup, before it is run (instead of the capability which +is obtained during Thread creation). \end{itemize} \subsection{Page and Cappage} @@ -317,4 +338,14 @@ Operations or capability objects: \item Get a copy of the capability. \end{itemize} +\section{Interface classes} +Around Iris is a system of some programs to create the operating system. These +include the device drivers. While Iris itself needs no specific interfaces +from them, some interface classes are defined, which are used by the default +environment. By defining classes, it is possible to let a program use any +device of that type without needing changes to its code. + +These definitions are in the source. A copy of the information here would only +lead to it getting outdated. + \end{document} diff --git a/report/making-of.tex b/report/making-of.tex index c5cc328..49052d1 100644 --- a/report/making-of.tex +++ b/report/making-of.tex @@ -578,4 +578,70 @@ Because this didn't feel good, I decided to implement the timer interrupt first. I copied some code for it from Linux and found, as I feared, that it didn't give any interrupts. I suppose the os timer isn't running either. +However, it wasn't as bad. I simply had a bug in my timer code; the OS timer +does give interrupts. Checks to see the random register also showed that while +it doesn't run as required by the mips specification, it does auto-increment as +part of the \textit{tlbwr} instruction, which is for practical purposes just as +good (but, I suppose, costs less power). + +So the only clock that isn't working is the cpu counter. This means that the +operating system timer must be used for timed interrupts, and that works fine. + +After rereading the Linux driver for the display, I also found several things I +had done wrong, and after fixing them it did indeed work. The display +controller reads its data from physical memory, which means that the entire +framebuffer needs to be a continuous part of physical memory. I had to create +a new kernel call for this. Like the other priviledged calls, I added it to +the Thread capability. + +\section{Debugging made easier} +So far, all debugging had to be done using blinking LEDs. This is slow and +annoying. With a working display, that was no longer needed. I added a simple +($6\cross8$) character set, and implemented a way to let the kernel send +messages which are printed on the screen. Then I changed the response to a +\textit{break} opcode to result in sending a character to the lcd as well. Now +I have a textual log output, which is much better than blinking LEDs. + +Shortly after this, I encountered a bug in the kernel allocation routines. +This still needed to be debugged with blinking LEDs, because allocation was +done as part of sending messages to the display driver. This was annoying, but +now that's done as well and the text log can be used. + +\section{Keyboard} +The keyboard driver works mostly as I expected it to. I added interrupts on +any change, so that it is quite normal that key changes are detected by +interrupt, which is faster than waiting for a scan. I would like to use +level-triggered interrupts, instead of edge-triggered. However, at the moment +that doesn't seem to work. I don't really care for now, although this may lead +to missed keys, so it is something to fix eventually. + +\section{What is a terminal?} +Now's the time to think about more high-level features. One feature I want to +have is that a user has a session manager. This session manager can have +access to the terminal. And it can lose it. And get it again. It gets access +when the user logs in, and loses it when the user logs out. Having access +means being able to use the hardware (display, keyboard, sound, etc.). The +problem is mostly with losing the display. Getting access to the display +happens by mapping the pages into memory. The user can then share these pages, +and it will be impossible to revoke the access. + +But there is a simple solution. The session manager itself is part of the +system. It is trusted by the kernel, and will behave. It will do anything the +user asks, as long as the system allows it. Each session manager can have its +own frame buffer. This is even a good idea: it means that user programs will +not have to handle things differently depending on whether the framebuffer is +available: it is always there, it may just not be visible for the user. Then +the rest of the problem is for the user to solve. The user may mess up their +own display if they want. It will not harm the main control display (used by +session managers and the login manager), or displays of other users. + +\section{Defining interfaces} +The next programs to write are a \textit{block device} (the mmc and sd card +driver, or else a ramdisk), a file system, the session manager, a keyboard +translator (interpreting modifier keys and implementing key repeat) and some +sort of shell. These are more device \textit{class} interfaces than simply +interfaces for these specific devices. So it's good to design an interface for +the class, which allows other devices of the same class to use the same +interface. Then higher level programs can use both devices without noticing. + \end{document}