1
0
Fork 0
mirror of https://github.com/Atmosphere-NX/Atmosphere.git synced 2024-11-05 19:51:45 +00:00

Implement all core Stage 1 logic for Fusee

This commit is contained in:
Michael Scire 2018-04-07 22:51:24 -06:00
parent 18f1274587
commit 33f76545df
19 changed files with 14671 additions and 31 deletions

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,53 @@
/*
* (C) Copyright 1997-2002 ELTEC Elektronik AG
* Frank Gottschling <fgottschling@eltec.de>
*
* See file CREDITS for list of people who contributed to this
* project.
*
* 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., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
#ifndef _VIDEO_FB_H_
#define _VIDEO_FB_H_
#define CONSOLE_BG_COL 0x00
#define CONSOLE_FG_COL 0xa0
/* Try using the small font */
#define CONFIG_VIDEO_FONT_SMALL
/*
* Graphic Data Format (GDF) bits for VIDEO_DATA_FORMAT
*/
#define GDF__8BIT_INDEX 0
#define GDF_15BIT_555RGB 1
#define GDF_16BIT_565RGB 2
#define GDF_32BIT_X888RGB 3
#define GDF_24BIT_888RGB 4
#define GDF__8BIT_332RGB 5
#define CONFIG_VIDEO_FB_LITTLE_ENDIAN
#define CONFIG_VIDEO_VISIBLE_COLS 720
#define CONFIG_VIDEO_VISIBLE_ROWS 1280
#define CONFIG_VIDEO_COLS 768
#define CONFIG_VIDEO_PIXEL_SIZE 4
#define CONFIG_VIDEO_DATA_FORMAT GDF_32BIT_X888RGB /* BGR actually, but w/e */
int video_init(void *fb);
void video_puts(const char *s);
#endif /*_VIDEO_FB_H_ */

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,269 @@
/* inih -- simple .INI file parser
inih is released under the New BSD license (see LICENSE.txt). Go to the project
home page for more info:
https://github.com/benhoyt/inih
*/
#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
#define _CRT_SECURE_NO_WARNINGS
#endif
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include "ini.h"
#if !INI_USE_STACK
#include <stdlib.h>
#endif
#define MAX_SECTION 50
#define MAX_NAME 50
/* Used by ini_parse_string() to keep track of string parsing state. */
typedef struct {
const char* ptr;
size_t num_left;
} ini_parse_string_ctx;
/* Strip whitespace chars off end of given string, in place. Return s. */
static char* rstrip(char* s)
{
char* p = s + strlen(s);
while (p > s && isspace((unsigned char)(*--p)))
*p = '\0';
return s;
}
/* Return pointer to first non-whitespace char in given string. */
static char* lskip(const char* s)
{
while (*s && isspace((unsigned char)(*s)))
s++;
return (char*)s;
}
/* Return pointer to first char (of chars) or inline comment in given string,
or pointer to null at end of string if neither found. Inline comment must
be prefixed by a whitespace character to register as a comment. */
static char* find_chars_or_comment(const char* s, const char* chars)
{
#if INI_ALLOW_INLINE_COMMENTS
int was_space = 0;
while (*s && (!chars || !strchr(chars, *s)) &&
!(was_space && strchr(INI_INLINE_COMMENT_PREFIXES, *s))) {
was_space = isspace((unsigned char)(*s));
s++;
}
#else
while (*s && (!chars || !strchr(chars, *s))) {
s++;
}
#endif
return (char*)s;
}
/* Version of strncpy that ensures dest (size bytes) is null-terminated. */
static char* strncpy0(char* dest, const char* src, size_t size)
{
strncpy(dest, src, size);
dest[size - 1] = '\0';
return dest;
}
/* See documentation in header file. */
int ini_parse_stream(ini_reader reader, void* stream, ini_handler handler,
void* user)
{
/* Uses a fair bit of stack (use heap instead if you need to) */
#if INI_USE_STACK
char line[INI_MAX_LINE];
int max_line = INI_MAX_LINE;
#else
char* line;
int max_line = INI_INITIAL_ALLOC;
#endif
#if INI_ALLOW_REALLOC
char* new_line;
int offset;
#endif
char section[MAX_SECTION] = "";
char prev_name[MAX_NAME] = "";
char* start;
char* end;
char* name;
char* value;
int lineno = 0;
int error = 0;
#if !INI_USE_STACK
line = (char*)malloc(INI_INITIAL_ALLOC);
if (!line) {
return -2;
}
#endif
#if INI_HANDLER_LINENO
#define HANDLER(u, s, n, v) handler(u, s, n, v, lineno)
#else
#define HANDLER(u, s, n, v) handler(u, s, n, v)
#endif
/* Scan through stream line by line */
while (reader(line, max_line, stream) != NULL) {
#if INI_ALLOW_REALLOC
offset = strlen(line);
while (offset == max_line - 1 && line[offset - 1] != '\n') {
max_line *= 2;
if (max_line > INI_MAX_LINE)
max_line = INI_MAX_LINE;
new_line = realloc(line, max_line);
if (!new_line) {
free(line);
return -2;
}
line = new_line;
if (reader(line + offset, max_line - offset, stream) == NULL)
break;
if (max_line >= INI_MAX_LINE)
break;
offset += strlen(line + offset);
}
#endif
lineno++;
start = line;
#if INI_ALLOW_BOM
if (lineno == 1 && (unsigned char)start[0] == 0xEF &&
(unsigned char)start[1] == 0xBB &&
(unsigned char)start[2] == 0xBF) {
start += 3;
}
#endif
start = lskip(rstrip(start));
if (strchr(INI_START_COMMENT_PREFIXES, *start)) {
/* Start-of-line comment */
}
#if INI_ALLOW_MULTILINE
else if (*prev_name && *start && start > line) {
/* Non-blank line with leading whitespace, treat as continuation
of previous name's value (as per Python configparser). */
if (!HANDLER(user, section, prev_name, start) && !error)
error = lineno;
}
#endif
else if (*start == '[') {
/* A "[section]" line */
end = find_chars_or_comment(start + 1, "]");
if (*end == ']') {
*end = '\0';
strncpy0(section, start + 1, sizeof(section));
*prev_name = '\0';
}
else if (!error) {
/* No ']' found on section line */
error = lineno;
}
}
else if (*start) {
/* Not a comment, must be a name[=:]value pair */
end = find_chars_or_comment(start, "=:");
if (*end == '=' || *end == ':') {
*end = '\0';
name = rstrip(start);
value = end + 1;
#if INI_ALLOW_INLINE_COMMENTS
end = find_chars_or_comment(value, NULL);
if (*end)
*end = '\0';
#endif
value = lskip(value);
rstrip(value);
/* Valid name[=:]value pair found, call handler */
strncpy0(prev_name, name, sizeof(prev_name));
if (!HANDLER(user, section, name, value) && !error)
error = lineno;
}
else if (!error) {
/* No '=' or ':' found on name[=:]value line */
error = lineno;
}
}
#if INI_STOP_ON_FIRST_ERROR
if (error)
break;
#endif
}
#if !INI_USE_STACK
free(line);
#endif
return error;
}
/* See documentation in header file. */
int ini_parse_file(FILE* file, ini_handler handler, void* user)
{
return ini_parse_stream((ini_reader)fgets, file, handler, user);
}
/* See documentation in header file. */
int ini_parse(const char* filename, ini_handler handler, void* user)
{
FILE* file;
int error;
file = fopen(filename, "r");
if (!file)
return -1;
error = ini_parse_file(file, handler, user);
fclose(file);
return error;
}
/* An ini_reader function to read the next line from a string buffer. This
is the fgets() equivalent used by ini_parse_string(). */
static char* ini_reader_string(char* str, int num, void* stream) {
ini_parse_string_ctx* ctx = (ini_parse_string_ctx*)stream;
const char* ctx_ptr = ctx->ptr;
size_t ctx_num_left = ctx->num_left;
char* strp = str;
char c;
if (ctx_num_left == 0 || num < 2)
return NULL;
while (num > 1 && ctx_num_left != 0) {
c = *ctx_ptr++;
ctx_num_left--;
*strp++ = c;
if (c == '\n')
break;
num--;
}
*strp = '\0';
ctx->ptr = ctx_ptr;
ctx->num_left = ctx_num_left;
return str;
}
/* See documentation in header file. */
int ini_parse_string(const char* string, ini_handler handler, void* user) {
ini_parse_string_ctx ctx;
ctx.ptr = string;
ctx.num_left = strlen(string);
return ini_parse_stream((ini_reader)ini_reader_string, &ctx, handler,
user);
}

View file

@ -0,0 +1,130 @@
/* inih -- simple .INI file parser
inih is released under the New BSD license (see LICENSE.txt). Go to the project
home page for more info:
https://github.com/benhoyt/inih
*/
#ifndef __INI_H__
#define __INI_H__
/* Make this header file easier to include in C++ code */
#ifdef __cplusplus
extern "C" {
#endif
#include <stdio.h>
/* Nonzero if ini_handler callback should accept lineno parameter. */
#ifndef INI_HANDLER_LINENO
#define INI_HANDLER_LINENO 0
#endif
/* Typedef for prototype of handler function. */
#if INI_HANDLER_LINENO
typedef int (*ini_handler)(void* user, const char* section,
const char* name, const char* value,
int lineno);
#else
typedef int (*ini_handler)(void* user, const char* section,
const char* name, const char* value);
#endif
/* Typedef for prototype of fgets-style reader function. */
typedef char* (*ini_reader)(char* str, int num, void* stream);
/* Parse given INI-style file. May have [section]s, name=value pairs
(whitespace stripped), and comments starting with ';' (semicolon). Section
is "" if name=value pair parsed before any section heading. name:value
pairs are also supported as a concession to Python's configparser.
For each name=value pair parsed, call handler function with given user
pointer as well as section, name, and value (data only valid for duration
of handler call). Handler should return nonzero on success, zero on error.
Returns 0 on success, line number of first error on parse error (doesn't
stop on first error), -1 on file open error, or -2 on memory allocation
error (only when INI_USE_STACK is zero).
*/
int ini_parse(const char* filename, ini_handler handler, void* user);
/* Same as ini_parse(), but takes a FILE* instead of filename. This doesn't
close the file when it's finished -- the caller must do that. */
int ini_parse_file(FILE* file, ini_handler handler, void* user);
/* Same as ini_parse(), but takes an ini_reader function pointer instead of
filename. Used for implementing custom or string-based I/O (see also
ini_parse_string). */
int ini_parse_stream(ini_reader reader, void* stream, ini_handler handler,
void* user);
/* Same as ini_parse(), but takes a zero-terminated string with the INI data
instead of a file. Useful for parsing INI data from a network socket or
already in memory. */
int ini_parse_string(const char* string, ini_handler handler, void* user);
/* Nonzero to allow multi-line value parsing, in the style of Python's
configparser. If allowed, ini_parse() will call the handler with the same
name for each subsequent line parsed. */
#ifndef INI_ALLOW_MULTILINE
#define INI_ALLOW_MULTILINE 1
#endif
/* Nonzero to allow a UTF-8 BOM sequence (0xEF 0xBB 0xBF) at the start of
the file. See http://code.google.com/p/inih/issues/detail?id=21 */
#ifndef INI_ALLOW_BOM
#define INI_ALLOW_BOM 1
#endif
/* Chars that begin a start-of-line comment. Per Python configparser, allow
both ; and # comments at the start of a line by default. */
#ifndef INI_START_COMMENT_PREFIXES
#define INI_START_COMMENT_PREFIXES ";#"
#endif
/* Nonzero to allow inline comments (with valid inline comment characters
specified by INI_INLINE_COMMENT_PREFIXES). Set to 0 to turn off and match
Python 3.2+ configparser behaviour. */
#ifndef INI_ALLOW_INLINE_COMMENTS
#define INI_ALLOW_INLINE_COMMENTS 1
#endif
#ifndef INI_INLINE_COMMENT_PREFIXES
#define INI_INLINE_COMMENT_PREFIXES ";"
#endif
/* Nonzero to use stack for line buffer, zero to use heap (malloc/free). */
#ifndef INI_USE_STACK
#define INI_USE_STACK 1
#endif
/* Maximum line length for any line in INI file (stack or heap). Note that
this must be 3 more than the longest line (due to '\r', '\n', and '\0'). */
#ifndef INI_MAX_LINE
#define INI_MAX_LINE 200
#endif
/* Nonzero to allow heap line buffer to grow via realloc(), zero for a
fixed-size buffer of INI_MAX_LINE bytes. Only applies if INI_USE_STACK is
zero. */
#ifndef INI_ALLOW_REALLOC
#define INI_ALLOW_REALLOC 0
#endif
/* Initial size in bytes for heap line buffer. Only applies if INI_USE_STACK
is zero. */
#ifndef INI_INITIAL_ALLOC
#define INI_INITIAL_ALLOC 200
#endif
/* Stop parsing on first error (default is to keep parsing). */
#ifndef INI_STOP_ON_FIRST_ERROR
#define INI_STOP_ON_FIRST_ERROR 0
#endif
#ifdef __cplusplus
}
#endif
#endif /* __INI_H__ */

View file

@ -0,0 +1,24 @@
/**
* Kernel print functions.
*/
#include "printk.h"
#include "vsprintf.h"
#include "../display/video_fb.h"
/**
* Temporary stand-in main printk.
*
* TODO: This should print via UART, console framebuffer, and to a ring for
* consumption by Horizon
*/
void printk(char *fmt, ...)
{
va_list list;
char buf[512];
va_start(list, fmt);
vsnprintf(buf, sizeof(buf), fmt, list);
video_puts(buf);
va_end(list);
}

View file

@ -0,0 +1,2 @@
void printk(char *fmt, ...);

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,25 @@
/*
* Copyright (C) 2011 Andrei Warkentin <andrey.warkentin@gmail.com>
*
* This program is free software ; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <stdarg.h>
#include <stdlib.h>
#ifndef VSPRINTF_H
#define VSPRINTF_H
struct va_format {
const char *fmt;
va_list *va;
};
unsigned long long simple_strtoull(const char *cp, char **endp, unsigned int base);
int vsnprintf(char *buf, size_t size, const char *fmt, va_list args);
int sscanf(const char *buf, const char *fmt, ...);
#endif /* VSPRINTF_H */

View file

@ -3,13 +3,34 @@
#include "fuse.h"
#include "se.h"
#include "sd_utils.h"
#include "stage2.h"
#include "lib/printk.h"
#include "display/video_fb.h"
#define LOADER_ADDRESS (void (*)(void))(0xFFF00000)
/* TODO: Should we allow more than 32K for the BCT0? */
#define BCT0_LOAD_ADDRESS (uintptr_t)(0x40038000)
#define BCT0_LOAD_END_ADDRESS (uintptr_t)(0x4003F000)
#define MAGIC_BCT0 0x30544342
void load_stage2(void *dst_address) {
if (!read_sd_file(dst_address, "fusee-loader.bin")) {
/* TODO: This is the failure condition. How should we panic? */
const char *load_config(void) {
if (!read_sd_file((void *)BCT0_LOAD_ADDRESS, BCT0_LOAD_END_ADDRESS - BCT0_LOAD_ADDRESS, "BCT.ini")) {
printk("Error: Failed to load BCT.ini!\n");
generic_panic();
}
if ((*((u32 *)(BCT0_LOAD_ADDRESS))) != MAGIC_BCT0) {
printk("Error: Unexpected magic in BCT.ini!\n");
generic_panic();
}
/* Return pointer to first line of the ini. */
const char *bct0 = (const char *)BCT0_LOAD_ADDRESS;
while (*bct0 && *bct0 != '\n') {
bct0++;
}
if (!bct0) {
printk("Error: BCT.ini has no newline!\n");
generic_panic();
}
return bct0;
}
void load_sbk(void) {
@ -25,21 +46,46 @@ void load_sbk(void) {
}
int main(void) {
void (*loader)(void) = LOADER_ADDRESS;
stage2_entrypoint_t stage2_entrypoint;
void **stage2_argv = (void **)(BCT0_LOAD_END_ADDRESS);
const char *bct0;
u32 *lfb_base;
/* Initialize DRAM. */
/* TODO: What can be stripped out to make this minimal? */
nx_hwinit();
/* Initialize the display. */
display_init();
/* Register the display as a printk provider. */
lfb_base = display_init_framebuffer();
video_init(lfb_base);
/* Say hello. */
printk("Welcome to Atmosph\xe8re Fus\xe9" "e!\n");
printk("Using color linear framebuffer at 0x%p!\n", lfb_base);
/* Try to load the SBK into the security engine, if possible. */
/* TODO: Should this be done later? */
load_sbk();
/* Load the loader payload into DRAM. */
load_stage2(LOADER_ADDRESS);
/* Load the BCT0 configuration ini off of the SD. */
bct0 = load_config();
/* TODO: Should the loader take argc/argv? */
loader();
/* Load the loader payload into DRAM. */
stage2_entrypoint = load_stage2(bct0);
/* Setup argv. */
memset(stage2_argv, 0, STAGE2_ARGC * sizeof(*stage2_argv));
stage2_argv[STAGE2_ARGV_VERSION] = &stage2_argv[STAGE2_ARGC];
*((u32 *)stage2_argv[STAGE2_ARGV_VERSION]) = 0;
stage2_argv[STAGE2_ARGV_CONFIG] = (void *)bct0;
stage2_argv[STAGE2_ARGV_LFB] = lfb_base;
/* Jump to Stage 2. */
stage2_entrypoint(STAGE2_ARGC, stage2_argv);
return 0;
}

View file

@ -1,8 +1,9 @@
#include "sd_utils.h"
int read_sd_file(void *dst, const char *filename) {
int read_sd_file(void *dst, size_t dst_size, const char *filename) {
/* TODO: Implement this function. */
(void)(dst);
(void)(dst_size);
(void)(filename);
/* Fail, because this function is unimplemented. */

View file

@ -3,6 +3,6 @@
#include "utils.h"
int read_sd_file(void *dst, const char *filename);
int read_sd_file(void *dst, size_t dst_size, const char *filename);
#endif

View file

@ -0,0 +1,53 @@
#include "utils.h"
#include "sd_utils.h"
#include "stage2.h"
#include "lib/printk.h"
#include "lib/vsprintf.h"
#include "lib/ini.h"
static int stage2_ini_handler(void *user, const char *section, const char *name, const char *value) {
stage2_config_t *config = (stage2_config_t *)user;
uintptr_t x = 0;
if (strcmp(section, "stage1") == 0) {
if (strcmp(name, STAGE2_NAME_KEY) == 0) {
strncpy(config->filename, value, sizeof(config->filename));
} else if (strcmp(name, STAGE2_ADDRESS_KEY) == 0) {
/* Read in load address as a hex string. */
sscanf(value, "%x", &x);
config->load_address = x;
if (config->entrypoint == NULL) {
config->entrypoint = (stage2_entrypoint_t)config->load_address;
}
} else if (strcmp(name, STAGE2_ENTRYPOINT_KEY) == 0) {
/* Read in entrypoint as a hex string. */
sscanf(value, "%x", &x);
config->entrypoint = (stage2_entrypoint_t)x;
} else {
return 0;
}
} else {
return 0;
}
return 1;
}
stage2_entrypoint_t load_stage2(const char *bct0) {
stage2_config_t config = {0};
if (ini_parse_string(bct0, stage2_ini_handler, &config) < 0) {
printk("Error: Failed to parse BCT.ini!\n");
generic_panic();
}
if (config.load_address == 0 || config.filename[0] == '\x00') {
printk("Error: Failed to determine where to load stage2!\n");
generic_panic();
}
if (!read_sd_file((void *)config.load_address, 0x100000, config.filename)) {
printk("Error: Failed to read stage2 (%s)!\n", config.filename);
generic_panic();
}
return config.entrypoint;
}

View file

@ -0,0 +1,27 @@
#ifndef FUSEE_STAGE2_H
#define FUSEE_STAGE2_H
#include "utils.h"
/* TODO: Is there a more concise way to do this? */
#define STAGE2_ARGV_VERSION 0
#define STAGE2_ARGV_CONFIG 1
#define STAGE2_ARGV_LFB 2
#define STAGE2_ARGC 3
#define STAGE2_NAME_KEY "stage2_file"
#define STAGE2_ADDRESS_KEY "stage2_addr"
#define STAGE2_ENTRYPOINT_KEY "stage2_entrypoint"
typedef void (*stage2_entrypoint_t)(int argc, void **argv);
typedef struct {
char filename[0x300];
uintptr_t load_address;
stage2_entrypoint_t entrypoint;
} stage2_config_t;
stage2_entrypoint_t load_stage2(const char *bct0);
#endif

View file

@ -1,7 +1,7 @@
#ifndef FUSEE_LOADER_H
#define FUSEE_LOADER_H
typedef void (*entrypoint_t)(int argc, const char *argv);
typedef void (*entrypoint_t)(int argc, void **argv);
entrypoint_t load_payload(void);

View file

@ -1,8 +1,9 @@
#include "sd_utils.h"
int read_sd_file(void *dst, const char *filename) {
int read_sd_file(void *dst, size_t dst_size, const char *filename) {
/* TODO: Implement this function. */
(void)(dst);
(void)(dst_size);
(void)(filename);
/* Fail, because this function is unimplemented. */

View file

@ -3,6 +3,6 @@
#include "utils.h"
int read_sd_file(void *dst, const char *filename);
int read_sd_file(void *dst, size_t dst_size, const char *filename);
#endif

View file

@ -5,23 +5,6 @@
int main(void) {
u32 *lfb_base;
nx_hwinit();
display_init();
// Set up the display, and register it as a printk provider.
lfb_base = display_init_framebuffer();
video_init(lfb_base);
// Always enable the backlight _after_ the display is initialized,
// to avoid flickering.
display_enable_backlight(true);
// Say hello.
printk("Welcome to Atmosph\xe8re Fus\xe9" "e!\n");
printk("Using color linear framebuffer at 0x%p!\n", lfb_base);
/* Do nothing for now */
return 0;
}