Compare commits

...

No commits in common. "1c7aa6edda81e2a2138a69f2b7880a2c83b5a14a" and "a5e4d1920066b074918662d75f2455e33051c215" have entirely different histories.

286 changed files with 13983 additions and 3143 deletions

13
.gitignore vendored
View File

@ -1,3 +1,14 @@
kern/compile/ /.depend
/.settings
/.project
/.cproject
/.root
/kern/compile/
/defs.mk
build
/userland/testbin/randcall/calls.c
/root/
*.swo
*.swp
build/* build/*
**/build **/build

View File

@ -46,7 +46,6 @@ tools:
build: build:
(cd userland && $(MAKE) build) (cd userland && $(MAKE) build)
(cd man && $(MAKE) install-staging) (cd man && $(MAKE) install-staging)
(cd testscripts && $(MAKE) build)
includes tags depend: includes tags depend:
(cd kern && $(MAKE) $@) (cd kern && $(MAKE) $@)

View File

@ -128,3 +128,4 @@ I would put the machine dependent stuffs in `kern/arch` and everything else outs
The function `splx()` and `splhigh()` is used to enable and restore interrupts The function `splx()` and `splhigh()` is used to enable and restore interrupts
This would not be sufficient to ensure mutual exclusion as we still need spinlocks. This would not be sufficient to ensure mutual exclusion as we still need spinlocks.

View File

@ -0,0 +1,46 @@
#include <stdio.h>
#include <stdarg.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <kern/secret.h>
#ifdef HOST
#include "hostcompat.h"
#endif
/* printf variant that is quiet during automated testing */
int
tprintf(const char *fmt, ...)
{
int chars;
va_list ap;
#ifdef SECRET_TESTING
return 0;
#endif
va_start(ap, fmt);
chars = vprintf(fmt, ap);
va_end(ap);
return chars;
}
/* printf variant that is loud during automated testing */
int
nprintf(const char *fmt, ...)
{
int chars;
va_list ap;
#ifndef SECRET_TESTING
return 0;
#endif
va_start(ap, fmt);
chars = vprintf(fmt, ap);
va_end(ap);
return chars;
}

2511
common/libtest161/config.h Normal file

File diff suppressed because it is too large Load Diff

206
common/libtest161/secure.c Normal file
View File

@ -0,0 +1,206 @@
// Beware, this code is shared between the kernel and userspace.
#ifdef _KERNEL
#include <types.h>
#include <lib.h>
#include <kern/errno.h>
#else
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <time.h>
#include <stdio.h>
#endif
#include <kern/secure.h>
#include "sha256.h"
// The full length (with null) of the hex string of a byte array
#define HEXLEN(a) 2*a+1
#define SHA256_BLOCK_SIZE 64
#define SHA256_OUTPUT_SIZE 32
// Keep this divisible by 4, or else change make_salt()
#define SALT_BYTES 8
// inner and outer padding for HMAC.
static const unsigned char ipad[SHA256_BLOCK_SIZE] = { [0 ... SHA256_BLOCK_SIZE-1] = 0x36 };
static const unsigned char opad[SHA256_BLOCK_SIZE] = { [0 ... SHA256_BLOCK_SIZE-1] = 0x5c };
// Hack for not having a userspace malloc until ASST3. We 'allocate' these statuc buffers.
// This works because the process single-threaded.
#define NUM_BUFFERS 4
#define BUFFER_LEN 1024
static char temp_buffers[NUM_BUFFERS][BUFFER_LEN];
static int buf_num = 0;
#ifndef _KERNEL
static int did_random = 0;
#define NSEC_PER_MSEC 1000000ULL
#define MSEC_PER_SEC 1000ULL
#endif
// Both userspace and the kernel are using the temp buffers now.
static void * _alloc(size_t size)
{
(void)size;
void *ptr = temp_buffers[buf_num];
buf_num++;
buf_num = buf_num % NUM_BUFFERS;
return ptr;
}
static void _free(void *ptr)
{
(void)ptr;
}
/*
* hamc_sha256 follows FIPS 198-1 HMAC using sha256.
* See http://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.180-4.pdf for details.
* NOTE: This is only thread-safe if called from within secprintf()!!!
*/
static int hmac_sha256(const char *msg, size_t msg_len, const char *key, size_t key_len,
unsigned char output[SHA256_OUTPUT_SIZE])
{
// We use a total of 320 bytes of array data on the stack
unsigned char k0[SHA256_BLOCK_SIZE];
// Steps 1-3. Anything less than 64 bytes gets 0s appended.
memset(k0, 0, SHA256_BLOCK_SIZE);
if (key_len <= SHA256_BLOCK_SIZE) {
memcpy(k0, key, key_len);
} else {
mbedtls_sha256((unsigned char *)key, key_len, k0, 0);
}
// Steps 4 and 7.
unsigned char k_ipad[SHA256_BLOCK_SIZE];
unsigned char k_opad[SHA256_BLOCK_SIZE];
int i;
for (i = 0; i < SHA256_BLOCK_SIZE; i++) {
k_ipad[i] = k0[i] ^ ipad[i];
k_opad[i] = k0[i] ^ opad[i];
}
// Step 5 (K0 xor ipad) || msg
// We have no idea how big the message is so we allocate this on the heap.
unsigned char *data = (unsigned char *)_alloc(msg_len + SHA256_BLOCK_SIZE);
if (!data)
return ENOMEM;
memcpy(data, k_ipad, SHA256_BLOCK_SIZE);
memcpy(data+SHA256_BLOCK_SIZE, msg, msg_len);
// Step6: H((k0 xor ipad) || msg)
unsigned char h1[SHA256_OUTPUT_SIZE];
mbedtls_sha256(data, msg_len + SHA256_BLOCK_SIZE, h1, 0);
_free(data);
// Step 8: (k0 xor opad) || H((k0 xor ipad) || msg)
unsigned char inner[SHA256_OUTPUT_SIZE + SHA256_BLOCK_SIZE];
memcpy(inner, k_opad, SHA256_BLOCK_SIZE);
memcpy(inner + SHA256_BLOCK_SIZE, h1, SHA256_OUTPUT_SIZE);
// Step 9: Finally, H((k0 xor opad) || H((k0 xor ipad) || msg))
mbedtls_sha256(inner, SHA256_OUTPUT_SIZE + SHA256_BLOCK_SIZE, output, 0);
return 0;
}
static inline char to_hex(int n)
{
return n < 10 ? '0'+n : 'a' + (n-10);
}
static void array_to_hex(unsigned char *a, size_t len, char *res)
{
size_t i, j;
j = 0;
for (i = 0; i < len; i++) {
res[j++] = to_hex(a[i] >> 4);
res[j++] = to_hex(a[i] & 0xF);
}
res[j] = '\0';
}
static void make_salt(char *salt_str)
{
#ifndef _KERNEL
if (!did_random) {
did_random = 1;
time_t sec;
unsigned long ns;
unsigned long long ms;
__time(&sec, &ns);
ms = (unsigned long long)sec * MSEC_PER_SEC;
ms += (ns / NSEC_PER_MSEC);
srandom((unsigned long)ms);
}
#endif
// Compute salt value
uint32_t salt[SALT_BYTES/sizeof(uint32_t)];
size_t i;
for (i = 0; i < SALT_BYTES/sizeof(uint32_t); i++)
{
salt[i] = random();
}
// Convert to hex string
array_to_hex((unsigned char *)salt, SALT_BYTES, salt_str);
}
int hmac(const char *msg, size_t msg_len, const char *key, size_t key_len,
char **hash_str)
{
*hash_str = _alloc(HEXLEN(SHA256_OUTPUT_SIZE));
if (!(*hash_str))
return ENOMEM;
// Hash it
unsigned char hash[SHA256_OUTPUT_SIZE];
int res = hmac_sha256(msg, msg_len, key, key_len, hash);
if (res)
return res;
// Convert to hex string
array_to_hex(hash, SHA256_OUTPUT_SIZE, *hash_str);
return 0;
}
// NOTE: This is only thread-safe if called from within secprintf()!!!
int hmac_salted(const char *msg, size_t msg_len, const char *key, size_t key_len,
char **hash_str, char **salt_str)
{
*salt_str = _alloc(HEXLEN(SALT_BYTES));
if (!(*salt_str))
return ENOMEM;
// Create the salt value
make_salt(*salt_str);
// Concatenate the key and salt, with the resulting string being null-terminated
size_t key2_len = key_len + HEXLEN(SALT_BYTES)-1;
char *key2 = (char *)_alloc(key2_len+1);
if (!key2)
return ENOMEM;
strcpy(key2, key);
strcpy(key2+key_len, *salt_str);
key2[key2_len] = '\0';
// Hash it
return hmac(msg, msg_len, key2, key2_len, hash_str);
}

451
common/libtest161/sha256.c Normal file
View File

@ -0,0 +1,451 @@
/*
* FIPS-180-2 compliant SHA-256 implementation
*
* Copyright (C) 2006-2015, ARM Limited, All Rights Reserved
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* This file is part of mbed TLS (https://tls.mbed.org)
*/
/*
* The SHA-256 Secure Hash Standard was published by NIST in 2002.
*
* http://csrc.nist.gov/publications/fips/fips180-2/fips180-2.pdf
*/
#if !defined(MBEDTLS_CONFIG_FILE)
#include "config.h"
#else
#include MBEDTLS_CONFIG_FILE
#endif
#if defined(MBEDTLS_SHA256_C)
#include "sha256.h"
#ifdef _KERNEL
#include <types.h>
#include <lib.h>
#else
#include <types/size_t.h>
#include <string.h>
#endif // _KERNEL
#if defined(MBEDTLS_SELF_TEST)
#if defined(MBEDTLS_PLATFORM_C)
#include "platform.h"
#else
#include <stdio.h>
#define mbedtls_printf printf
#endif /* MBEDTLS_PLATFORM_C */
#endif /* MBEDTLS_SELF_TEST */
#if !defined(MBEDTLS_SHA256_ALT)
/* Implementation that should never be optimized out by the compiler */
static void mbedtls_zeroize( void *v, size_t n ) {
volatile unsigned char *p = v; while( n-- ) *p++ = 0;
}
/*
* 32-bit integer manipulation macros (big endian)
*/
#ifndef GET_UINT32_BE
#define GET_UINT32_BE(n,b,i) \
do { \
(n) = ( (uint32_t) (b)[(i) ] << 24 ) \
| ( (uint32_t) (b)[(i) + 1] << 16 ) \
| ( (uint32_t) (b)[(i) + 2] << 8 ) \
| ( (uint32_t) (b)[(i) + 3] ); \
} while( 0 )
#endif
#ifndef PUT_UINT32_BE
#define PUT_UINT32_BE(n,b,i) \
do { \
(b)[(i) ] = (unsigned char) ( (n) >> 24 ); \
(b)[(i) + 1] = (unsigned char) ( (n) >> 16 ); \
(b)[(i) + 2] = (unsigned char) ( (n) >> 8 ); \
(b)[(i) + 3] = (unsigned char) ( (n) ); \
} while( 0 )
#endif
void mbedtls_sha256_init( mbedtls_sha256_context *ctx )
{
memset( ctx, 0, sizeof( mbedtls_sha256_context ) );
}
void mbedtls_sha256_free( mbedtls_sha256_context *ctx )
{
if( ctx == NULL )
return;
mbedtls_zeroize( ctx, sizeof( mbedtls_sha256_context ) );
}
void mbedtls_sha256_clone( mbedtls_sha256_context *dst,
const mbedtls_sha256_context *src )
{
*dst = *src;
}
/*
* SHA-256 context setup
*/
void mbedtls_sha256_starts( mbedtls_sha256_context *ctx, int is224 )
{
ctx->total[0] = 0;
ctx->total[1] = 0;
if( is224 == 0 )
{
/* SHA-256 */
ctx->state[0] = 0x6A09E667;
ctx->state[1] = 0xBB67AE85;
ctx->state[2] = 0x3C6EF372;
ctx->state[3] = 0xA54FF53A;
ctx->state[4] = 0x510E527F;
ctx->state[5] = 0x9B05688C;
ctx->state[6] = 0x1F83D9AB;
ctx->state[7] = 0x5BE0CD19;
}
else
{
/* SHA-224 */
ctx->state[0] = 0xC1059ED8;
ctx->state[1] = 0x367CD507;
ctx->state[2] = 0x3070DD17;
ctx->state[3] = 0xF70E5939;
ctx->state[4] = 0xFFC00B31;
ctx->state[5] = 0x68581511;
ctx->state[6] = 0x64F98FA7;
ctx->state[7] = 0xBEFA4FA4;
}
ctx->is224 = is224;
}
#if !defined(MBEDTLS_SHA256_PROCESS_ALT)
static const uint32_t K[] =
{
0x428A2F98, 0x71374491, 0xB5C0FBCF, 0xE9B5DBA5,
0x3956C25B, 0x59F111F1, 0x923F82A4, 0xAB1C5ED5,
0xD807AA98, 0x12835B01, 0x243185BE, 0x550C7DC3,
0x72BE5D74, 0x80DEB1FE, 0x9BDC06A7, 0xC19BF174,
0xE49B69C1, 0xEFBE4786, 0x0FC19DC6, 0x240CA1CC,
0x2DE92C6F, 0x4A7484AA, 0x5CB0A9DC, 0x76F988DA,
0x983E5152, 0xA831C66D, 0xB00327C8, 0xBF597FC7,
0xC6E00BF3, 0xD5A79147, 0x06CA6351, 0x14292967,
0x27B70A85, 0x2E1B2138, 0x4D2C6DFC, 0x53380D13,
0x650A7354, 0x766A0ABB, 0x81C2C92E, 0x92722C85,
0xA2BFE8A1, 0xA81A664B, 0xC24B8B70, 0xC76C51A3,
0xD192E819, 0xD6990624, 0xF40E3585, 0x106AA070,
0x19A4C116, 0x1E376C08, 0x2748774C, 0x34B0BCB5,
0x391C0CB3, 0x4ED8AA4A, 0x5B9CCA4F, 0x682E6FF3,
0x748F82EE, 0x78A5636F, 0x84C87814, 0x8CC70208,
0x90BEFFFA, 0xA4506CEB, 0xBEF9A3F7, 0xC67178F2,
};
#define SHR(x,n) ((x & 0xFFFFFFFF) >> n)
#define ROTR(x,n) (SHR(x,n) | (x << (32 - n)))
#define S0(x) (ROTR(x, 7) ^ ROTR(x,18) ^ SHR(x, 3))
#define S1(x) (ROTR(x,17) ^ ROTR(x,19) ^ SHR(x,10))
#define S2(x) (ROTR(x, 2) ^ ROTR(x,13) ^ ROTR(x,22))
#define S3(x) (ROTR(x, 6) ^ ROTR(x,11) ^ ROTR(x,25))
#define F0(x,y,z) ((x & y) | (z & (x | y)))
#define F1(x,y,z) (z ^ (x & (y ^ z)))
#define R(t) \
( \
W[t] = S1(W[t - 2]) + W[t - 7] + \
S0(W[t - 15]) + W[t - 16] \
)
#define P(a,b,c,d,e,f,g,h,x,K) \
{ \
temp1 = h + S3(e) + F1(e,f,g) + K + x; \
temp2 = S2(a) + F0(a,b,c); \
d += temp1; h = temp1 + temp2; \
}
void mbedtls_sha256_process( mbedtls_sha256_context *ctx, const unsigned char data[64] )
{
uint32_t temp1, temp2, W[64];
uint32_t A[8];
unsigned int i;
for( i = 0; i < 8; i++ )
A[i] = ctx->state[i];
#if defined(MBEDTLS_SHA256_SMALLER)
for( i = 0; i < 64; i++ )
{
if( i < 16 )
GET_UINT32_BE( W[i], data, 4 * i );
else
R( i );
P( A[0], A[1], A[2], A[3], A[4], A[5], A[6], A[7], W[i], K[i] );
temp1 = A[7]; A[7] = A[6]; A[6] = A[5]; A[5] = A[4]; A[4] = A[3];
A[3] = A[2]; A[2] = A[1]; A[1] = A[0]; A[0] = temp1;
}
#else /* MBEDTLS_SHA256_SMALLER */
for( i = 0; i < 16; i++ )
GET_UINT32_BE( W[i], data, 4 * i );
for( i = 0; i < 16; i += 8 )
{
P( A[0], A[1], A[2], A[3], A[4], A[5], A[6], A[7], W[i+0], K[i+0] );
P( A[7], A[0], A[1], A[2], A[3], A[4], A[5], A[6], W[i+1], K[i+1] );
P( A[6], A[7], A[0], A[1], A[2], A[3], A[4], A[5], W[i+2], K[i+2] );
P( A[5], A[6], A[7], A[0], A[1], A[2], A[3], A[4], W[i+3], K[i+3] );
P( A[4], A[5], A[6], A[7], A[0], A[1], A[2], A[3], W[i+4], K[i+4] );
P( A[3], A[4], A[5], A[6], A[7], A[0], A[1], A[2], W[i+5], K[i+5] );
P( A[2], A[3], A[4], A[5], A[6], A[7], A[0], A[1], W[i+6], K[i+6] );
P( A[1], A[2], A[3], A[4], A[5], A[6], A[7], A[0], W[i+7], K[i+7] );
}
for( i = 16; i < 64; i += 8 )
{
P( A[0], A[1], A[2], A[3], A[4], A[5], A[6], A[7], R(i+0), K[i+0] );
P( A[7], A[0], A[1], A[2], A[3], A[4], A[5], A[6], R(i+1), K[i+1] );
P( A[6], A[7], A[0], A[1], A[2], A[3], A[4], A[5], R(i+2), K[i+2] );
P( A[5], A[6], A[7], A[0], A[1], A[2], A[3], A[4], R(i+3), K[i+3] );
P( A[4], A[5], A[6], A[7], A[0], A[1], A[2], A[3], R(i+4), K[i+4] );
P( A[3], A[4], A[5], A[6], A[7], A[0], A[1], A[2], R(i+5), K[i+5] );
P( A[2], A[3], A[4], A[5], A[6], A[7], A[0], A[1], R(i+6), K[i+6] );
P( A[1], A[2], A[3], A[4], A[5], A[6], A[7], A[0], R(i+7), K[i+7] );
}
#endif /* MBEDTLS_SHA256_SMALLER */
for( i = 0; i < 8; i++ )
ctx->state[i] += A[i];
}
#endif /* !MBEDTLS_SHA256_PROCESS_ALT */
/*
* SHA-256 process buffer
*/
void mbedtls_sha256_update( mbedtls_sha256_context *ctx, const unsigned char *input,
size_t ilen )
{
size_t fill;
uint32_t left;
if( ilen == 0 )
return;
left = ctx->total[0] & 0x3F;
fill = 64 - left;
ctx->total[0] += (uint32_t) ilen;
ctx->total[0] &= 0xFFFFFFFF;
if( ctx->total[0] < (uint32_t) ilen )
ctx->total[1]++;
if( left && ilen >= fill )
{
memcpy( (void *) (ctx->buffer + left), input, fill );
mbedtls_sha256_process( ctx, ctx->buffer );
input += fill;
ilen -= fill;
left = 0;
}
while( ilen >= 64 )
{
mbedtls_sha256_process( ctx, input );
input += 64;
ilen -= 64;
}
if( ilen > 0 )
memcpy( (void *) (ctx->buffer + left), input, ilen );
}
static const unsigned char sha256_padding[64] =
{
0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
/*
* SHA-256 final digest
*/
void mbedtls_sha256_finish( mbedtls_sha256_context *ctx, unsigned char output[32] )
{
uint32_t last, padn;
uint32_t high, low;
unsigned char msglen[8];
high = ( ctx->total[0] >> 29 )
| ( ctx->total[1] << 3 );
low = ( ctx->total[0] << 3 );
PUT_UINT32_BE( high, msglen, 0 );
PUT_UINT32_BE( low, msglen, 4 );
last = ctx->total[0] & 0x3F;
padn = ( last < 56 ) ? ( 56 - last ) : ( 120 - last );
mbedtls_sha256_update( ctx, sha256_padding, padn );
mbedtls_sha256_update( ctx, msglen, 8 );
PUT_UINT32_BE( ctx->state[0], output, 0 );
PUT_UINT32_BE( ctx->state[1], output, 4 );
PUT_UINT32_BE( ctx->state[2], output, 8 );
PUT_UINT32_BE( ctx->state[3], output, 12 );
PUT_UINT32_BE( ctx->state[4], output, 16 );
PUT_UINT32_BE( ctx->state[5], output, 20 );
PUT_UINT32_BE( ctx->state[6], output, 24 );
if( ctx->is224 == 0 )
PUT_UINT32_BE( ctx->state[7], output, 28 );
}
#endif /* !MBEDTLS_SHA256_ALT */
/*
* output = SHA-256( input buffer )
*/
void mbedtls_sha256( const unsigned char *input, size_t ilen,
unsigned char output[32], int is224 )
{
mbedtls_sha256_context ctx;
mbedtls_sha256_init( &ctx );
mbedtls_sha256_starts( &ctx, is224 );
mbedtls_sha256_update( &ctx, input, ilen );
mbedtls_sha256_finish( &ctx, output );
mbedtls_sha256_free( &ctx );
}
#if defined(MBEDTLS_SELF_TEST)
/*
* FIPS-180-2 test vectors
*/
static const unsigned char sha256_test_buf[3][57] =
{
{ "abc" },
{ "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" },
{ "" }
};
static const int sha256_test_buflen[3] =
{
3, 56, 1000
};
static const unsigned char sha256_test_sum[6][32] =
{
/*
* SHA-224 test vectors
*/
{ 0x23, 0x09, 0x7D, 0x22, 0x34, 0x05, 0xD8, 0x22,
0x86, 0x42, 0xA4, 0x77, 0xBD, 0xA2, 0x55, 0xB3,
0x2A, 0xAD, 0xBC, 0xE4, 0xBD, 0xA0, 0xB3, 0xF7,
0xE3, 0x6C, 0x9D, 0xA7 },
{ 0x75, 0x38, 0x8B, 0x16, 0x51, 0x27, 0x76, 0xCC,
0x5D, 0xBA, 0x5D, 0xA1, 0xFD, 0x89, 0x01, 0x50,
0xB0, 0xC6, 0x45, 0x5C, 0xB4, 0xF5, 0x8B, 0x19,
0x52, 0x52, 0x25, 0x25 },
{ 0x20, 0x79, 0x46, 0x55, 0x98, 0x0C, 0x91, 0xD8,
0xBB, 0xB4, 0xC1, 0xEA, 0x97, 0x61, 0x8A, 0x4B,
0xF0, 0x3F, 0x42, 0x58, 0x19, 0x48, 0xB2, 0xEE,
0x4E, 0xE7, 0xAD, 0x67 },
/*
* SHA-256 test vectors
*/
{ 0xBA, 0x78, 0x16, 0xBF, 0x8F, 0x01, 0xCF, 0xEA,
0x41, 0x41, 0x40, 0xDE, 0x5D, 0xAE, 0x22, 0x23,
0xB0, 0x03, 0x61, 0xA3, 0x96, 0x17, 0x7A, 0x9C,
0xB4, 0x10, 0xFF, 0x61, 0xF2, 0x00, 0x15, 0xAD },
{ 0x24, 0x8D, 0x6A, 0x61, 0xD2, 0x06, 0x38, 0xB8,
0xE5, 0xC0, 0x26, 0x93, 0x0C, 0x3E, 0x60, 0x39,
0xA3, 0x3C, 0xE4, 0x59, 0x64, 0xFF, 0x21, 0x67,
0xF6, 0xEC, 0xED, 0xD4, 0x19, 0xDB, 0x06, 0xC1 },
{ 0xCD, 0xC7, 0x6E, 0x5C, 0x99, 0x14, 0xFB, 0x92,
0x81, 0xA1, 0xC7, 0xE2, 0x84, 0xD7, 0x3E, 0x67,
0xF1, 0x80, 0x9A, 0x48, 0xA4, 0x97, 0x20, 0x0E,
0x04, 0x6D, 0x39, 0xCC, 0xC7, 0x11, 0x2C, 0xD0 }
};
/*
* Checkup routine
*/
int mbedtls_sha256_self_test( int verbose )
{
int i, j, k, buflen, ret = 0;
unsigned char buf[1024];
unsigned char sha256sum[32];
mbedtls_sha256_context ctx;
mbedtls_sha256_init( &ctx );
for( i = 0; i < 6; i++ )
{
j = i % 3;
k = i < 3;
if( verbose != 0 )
mbedtls_printf( " SHA-%d test #%d: ", 256 - k * 32, j + 1 );
mbedtls_sha256_starts( &ctx, k );
if( j == 2 )
{
memset( buf, 'a', buflen = 1000 );
for( j = 0; j < 1000; j++ )
mbedtls_sha256_update( &ctx, buf, buflen );
}
else
mbedtls_sha256_update( &ctx, sha256_test_buf[j],
sha256_test_buflen[j] );
mbedtls_sha256_finish( &ctx, sha256sum );
if( memcmp( sha256sum, sha256_test_sum[i], 32 - k * 4 ) != 0 )
{
if( verbose != 0 )
mbedtls_printf( "failed\n" );
ret = 1;
goto exit;
}
if( verbose != 0 )
mbedtls_printf( "passed\n" );
}
if( verbose != 0 )
mbedtls_printf( "\n" );
exit:
mbedtls_sha256_free( &ctx );
return( ret );
}
#endif /* MBEDTLS_SELF_TEST */
#endif /* MBEDTLS_SHA256_C */

146
common/libtest161/sha256.h Normal file
View File

@ -0,0 +1,146 @@
/**
* \file sha256.h
*
* \brief SHA-224 and SHA-256 cryptographic hash function
*
* Copyright (C) 2006-2015, ARM Limited, All Rights Reserved
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* This file is part of mbed TLS (https://tls.mbed.org)
*/
#ifndef MBEDTLS_SHA256_H
#define MBEDTLS_SHA256_H
#if !defined(MBEDTLS_CONFIG_FILE)
#include "config.h"
#else
#include MBEDTLS_CONFIG_FILE
#endif
#ifdef _KERNEL
#include <types.h>
#include <lib.h>
#else
#include <types/size_t.h>
#include <stdint.h>
#endif //_KERNEL
#if !defined(MBEDTLS_SHA256_ALT)
// Regular implementation
//
#ifdef __cplusplus
extern "C" {
#endif
/**
* \brief SHA-256 context structure
*/
typedef struct
{
uint32_t total[2]; /*!< number of bytes processed */
uint32_t state[8]; /*!< intermediate digest state */
unsigned char buffer[64]; /*!< data block being processed */
int is224; /*!< 0 => SHA-256, else SHA-224 */
}
mbedtls_sha256_context;
/**
* \brief Initialize SHA-256 context
*
* \param ctx SHA-256 context to be initialized
*/
void mbedtls_sha256_init( mbedtls_sha256_context *ctx );
/**
* \brief Clear SHA-256 context
*
* \param ctx SHA-256 context to be cleared
*/
void mbedtls_sha256_free( mbedtls_sha256_context *ctx );
/**
* \brief Clone (the state of) a SHA-256 context
*
* \param dst The destination context
* \param src The context to be cloned
*/
void mbedtls_sha256_clone( mbedtls_sha256_context *dst,
const mbedtls_sha256_context *src );
/**
* \brief SHA-256 context setup
*
* \param ctx context to be initialized
* \param is224 0 = use SHA256, 1 = use SHA224
*/
void mbedtls_sha256_starts( mbedtls_sha256_context *ctx, int is224 );
/**
* \brief SHA-256 process buffer
*
* \param ctx SHA-256 context
* \param input buffer holding the data
* \param ilen length of the input data
*/
void mbedtls_sha256_update( mbedtls_sha256_context *ctx, const unsigned char *input,
size_t ilen );
/**
* \brief SHA-256 final digest
*
* \param ctx SHA-256 context
* \param output SHA-224/256 checksum result
*/
void mbedtls_sha256_finish( mbedtls_sha256_context *ctx, unsigned char output[32] );
/* Internal use */
void mbedtls_sha256_process( mbedtls_sha256_context *ctx, const unsigned char data[64] );
#ifdef __cplusplus
}
#endif
#else /* MBEDTLS_SHA256_ALT */
#include "sha256_alt.h"
#endif /* MBEDTLS_SHA256_ALT */
#ifdef __cplusplus
extern "C" {
#endif
/**
* \brief Output = SHA-256( input buffer )
*
* \param input buffer holding the data
* \param ilen length of the input data
* \param output SHA-224/256 checksum result
* \param is224 0 = use SHA256, 1 = use SHA224
*/
void mbedtls_sha256( const unsigned char *input, size_t ilen,
unsigned char output[32], int is224 );
/**
* \brief Checkup routine
*
* \return 0 if successful, or 1 if the test failed
*/
int mbedtls_sha256_self_test( int verbose );
#ifdef __cplusplus
}
#endif
#endif /* mbedtls_sha256.h */

188
common/libtest161/test161.c Normal file
View File

@ -0,0 +1,188 @@
// Beware, this code is shared between the kernel and userspace.
#ifdef _KERNEL
#include <types.h>
#include <lib.h>
#include <synch.h>
#include <kern/errno.h>
#include <kern/secure.h>
#include <kern/test161.h>
#else
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <errno.h>
#include <unistd.h>
#include <test161/test161.h>
#include <test161/secure.h>
#endif
// Hack for allocating userspace memory without malloc, and for
// allowing secprintf in kmalloc when we're out of memory.
#define BUFFER_SIZE 1024
static char temp_buffer[BUFFER_SIZE];
#ifndef _KERNEL
static char write_buffer[BUFFER_SIZE];
#endif
#ifdef _KERNEL
// secprintf needs to be synchronized in the kernel because multiple threads
// may be trying to secprintf at the same time.
static struct semaphore *test161_sem;
#endif
// For now, allocating just passes a reference to our static temp buffer, and
// free does nothing.
static inline void * _alloc()
{
return temp_buffer;
}
static inline void _free(void *ptr)
{
(void)ptr;
}
/*
* Common success function for kernel tests. If SECRET_TESTING is defined,
* ksecprintf will compute the hmac/sha256 hash of any message using the
* shared secret and a random salt value. The (secure) server also knows
* the secret and can verify the message was generated by a trusted source.
* The salt value prevents against replay attacks.
*/
int
success(int status, const char * secret, const char * name) {
if (status == TEST161_SUCCESS) {
return secprintf(secret, "SUCCESS", name);
} else {
return secprintf(secret, "FAIL", name);
}
}
int
partial_credit(const char *secret, const char *name, int scored, int total)
{
char buffer[128];
snprintf(buffer, 128, "PARTIAL CREDIT %d OF %d", scored, total);
return secprintf(secret, buffer, name);
}
#ifndef _KERNEL
// Borrowed from parallelvm. We need atomic console writes so our
// output doesn't get intermingled since test161 works with lines.
static
int
say(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
vsnprintf(write_buffer, BUFFER_SIZE, fmt, ap);
va_end(ap);
return write(STDOUT_FILENO, write_buffer, strlen(write_buffer));
}
#endif
#ifndef SECRET_TESTING
int
snsecprintf(size_t len, char *buffer, const char *secret, const char *msg, const char *name)
{
(void)secret;
return snprintf(buffer, len, "%s: %s", name, msg);
}
int
secprintf(const char * secret, const char * msg, const char * name)
{
(void)secret;
#ifdef _KERNEL
return kprintf("\n%s: %s\n", name, msg);
#else
return say("\n%s: %s\n", name, msg);
#endif
}
#else
static int
secprintf_common(int use_buf, size_t b_len, char *buffer,
const char *secret, const char *msg, const char *name)
{
char *hash, *salt, *fullmsg;
int res;
size_t len;
#ifdef _KERNEL
if (test161_sem == NULL) {
panic("test161_sem is NULL. Your kernel is missing test161_bootstrap.");
}
P(test161_sem);
#endif
hash = salt = fullmsg = NULL;
// test161 expects "name: msg"
len = strlen(name) + strlen(msg) + 3; // +3 for " :" and null terminator
fullmsg = (char *)_alloc(len);
if (fullmsg == NULL) {
res = -ENOMEM;
goto out;
}
snprintf(fullmsg, len, "%s: %s", name, msg);
res = hmac_salted(fullmsg, len-1, secret, strlen(secret), &hash, &salt);
if (res) {
res = -res;
goto out;
}
if (!use_buf) {
#ifdef _KERNEL
res = kprintf("\n(%s, %s, %s, %s: %s)\n", name, hash, salt, name, msg);
#else
res = say("\n(%s, %s, %s, %s: %s)\n", name, hash, salt, name, msg);
#endif
} else {
res = snprintf(buffer, b_len, "\n(%s, %s, %s, %s: %s)\n", name, hash, salt, name, msg);
}
out:
// These may be NULL, but that's OK
_free(hash);
_free(salt);
_free(fullmsg);
#ifdef _KERNEL
V(test161_sem);
#endif
return res;
}
int
snsecprintf(size_t b_len, char *buffer, const char *secret, const char *msg, const char *name)
{
return secprintf_common(1, b_len, buffer, secret, msg, name);
}
int
secprintf(const char * secret, const char * msg, const char * name)
{
return secprintf_common(0, 0, NULL, secret, msg, name);
}
#endif
#ifdef _KERNEL
void test161_bootstrap()
{
test161_sem = sem_create("test161", 1);
if (test161_sem == NULL) {
panic("Failed to create test161 secprintf semaphore");
}
}
#endif

28
configure vendored
View File

@ -29,9 +29,6 @@ DEBUG='-O2'
# when make runs rather than when this script runs. # when make runs rather than when this script runs.
OSTREE='$(HOME)/os161/root' OSTREE='$(HOME)/os161/root'
# By default don't explicitly configure a Python interpreter.
PYTHON_INTERPRETER=
# Assume this # Assume this
HOST_CC=gcc HOST_CC=gcc
@ -137,28 +134,6 @@ else
HOST_CFLAGS="${HOST_CFLAGS} -DDECLARE_NTOHLL" HOST_CFLAGS="${HOST_CFLAGS} -DDECLARE_NTOHLL"
fi fi
# Look for a Python interpreter.
PYTHON=
for PYNAME in \
python python2.7 \
/usr/local/bin/python /usr/local/bin/python2.7 \
/usr/pkg/bin/python /usr/pkg/bin/python2.7 \
/opt/bin/python /opt/bin/python2.7 \
; do
PYVERSION=`($PYNAME -V) 2>&1 || echo none`
case "$PYVERSION" in
Python\ 2.*|Python\ 3.*) PYTHON=$PYNAME;;
none) ;;
*) ;; # ?
esac
done
case "$PYTHON" in
python) ;;
/*) PYTHON_INTERPRETER="$PYTHON";;
*) PYTHON_INTERPRETER="/usr/bin/env $PYTHON";;
esac
#################### ####################
# Now generate defs.mk. # Now generate defs.mk.
@ -192,8 +167,5 @@ EOF
if [ "x$HOST_CFLAGS" != x ]; then if [ "x$HOST_CFLAGS" != x ]; then
echo "HOST_CFLAGS+=$HOST_CFLAGS" echo "HOST_CFLAGS+=$HOST_CFLAGS"
fi fi
if [ "x$PYTHON_INTERPRETER" != x ]; then
echo "PYTHON_INTERPRETER=$PYTHON_INTERPRETER"
fi
) > defs.mk ) > defs.mk

22
defs.mk
View File

@ -1,22 +0,0 @@
# This file was generated by configure. Edits will disappear if you rerun
# configure. If you find that you need to edit this file to make things
# work, let the course staff know and we'll try to fix the configure script.
#
# The purpose of this file is to hold all the makefile definitions
# needed to adjust the OS/161 build process to any particular
# environment. If I've done it right, all you need to do is rerun the
# configure script and make clean if you start working on a different
# host OS. If I've done it mostly right, you may need to edit this
# file but you still hopefully won't need to edit any of the
# makefiles.
#
# The things that can be set here are documented in mk/os161.config.mk.
#
OSTREE=/root/os161/root
PLATFORM=sys161
MACHINE=mips
COMPAT_CFLAGS= -DNEED_NTOHLL
COMPAT_TARGETS=
HOST_CFLAGS+= -DDECLARE_NTOHLL
PYTHON_INTERPRETER=/usr/bin/env

View File

@ -126,6 +126,15 @@ free_kpages(vaddr_t addr)
(void)addr; (void)addr;
} }
unsigned
int
coremap_used_bytes() {
/* dumbvm doesn't track page allocations. Return 0 so that khu works. */
return 0;
}
void void
vm_tlbshootdown(const struct tlbshootdown *ts) vm_tlbshootdown(const struct tlbshootdown *ts)
{ {

33
kern/conf/ASST1 Normal file
View File

@ -0,0 +1,33 @@
# Kernel config file using dumbvm.
# This should be used until you have your own VM system.
include conf/conf.kern # get definitions of available options
debug # Compile with debug info.
#
# Device drivers for hardware.
#
device lamebus0 # System/161 main bus
device emu* at lamebus* # Emulator passthrough filesystem
device ltrace* at lamebus* # trace161 trace control device
device ltimer* at lamebus* # Timer device
device lrandom* at lamebus* # Random device
device lhd* at lamebus* # Disk device
device lser* at lamebus* # Serial port
#device lscreen* at lamebus* # Text screen (not supported yet)
#device lnet* at lamebus* # Network interface (not supported yet)
device beep0 at ltimer* # Abstract beep handler device
device con0 at lser* # Abstract console on serial port
#device con0 at lscreen* # Abstract console on screen (not supported)
device rtclock0 at ltimer* # Abstract realtime clock
device random0 at lrandom* # Abstract randomness device
#options net # Network stack (not supported)
options semfs # Semaphores for userland
options sfs # Always use the file system
#options netfs # You might write this as a project.
options dumbvm # Chewing gum and baling wire.
options synchprobs # Uncomment to enable ASST1 synchronization problems

33
kern/conf/ASST2 Normal file
View File

@ -0,0 +1,33 @@
# Kernel config file using dumbvm.
# This should be used until you have your own VM system.
include conf/conf.kern # get definitions of available options
debug # Compile with debug info.
#
# Device drivers for hardware.
#
device lamebus0 # System/161 main bus
device emu* at lamebus* # Emulator passthrough filesystem
device ltrace* at lamebus* # trace161 trace control device
device ltimer* at lamebus* # Timer device
device lrandom* at lamebus* # Random device
device lhd* at lamebus* # Disk device
device lser* at lamebus* # Serial port
#device lscreen* at lamebus* # Text screen (not supported yet)
#device lnet* at lamebus* # Network interface (not supported yet)
device beep0 at ltimer* # Abstract beep handler device
device con0 at lser* # Abstract console on serial port
#device con0 at lscreen* # Abstract console on screen (not supported)
device rtclock0 at ltimer* # Abstract realtime clock
device random0 at lrandom* # Abstract randomness device
#options net # Network stack (not supported)
options semfs # Semaphores for userland
options sfs # Always use the file system
#options netfs # You might write this as a project.
options dumbvm # Chewing gum and baling wire.
#options synchprobs # Uncomment to enable ASST1 synchronization problems

33
kern/conf/ASST3 Normal file
View File

@ -0,0 +1,33 @@
# Kernel config file for an ordinary, generic kernel.
# This config file should be used once you start working on
# your own VM system.
include conf/conf.kern # get definitions of available options
debug # Compile with debug info.
#
# Device drivers for hardware.
#
device lamebus0 # System/161 main bus
device emu* at lamebus* # Emulator passthrough filesystem
device ltrace* at lamebus* # trace161 trace control device
device ltimer* at lamebus* # Timer device
device lrandom* at lamebus* # Random device
device lhd* at lamebus* # Disk device
device lser* at lamebus* # Serial port
#device lscreen* at lamebus* # Text screen (not supported yet)
#device lnet* at lamebus* # Network interface (not supported yet)
device beep0 at ltimer* # Abstract beep handler device
device con0 at lser* # Abstract console on serial port
#device con0 at lscreen* # Abstract console on screen (not supported)
device rtclock0 at ltimer* # Abstract realtime clock
device random0 at lrandom* # Abstract randomness device
#options net # Network stack (not supported)
options semfs # Semaphores for userland
options sfs # Always use the file system
#options netfs # You might write this as a project.
#options dumbvm # Use your own VM system now.

View File

@ -32,4 +32,4 @@ options sfs # Always use the file system
#options netfs # You might write this as a project. #options netfs # You might write this as a project.
options dumbvm # Chewing gum and baling wire. options dumbvm # Chewing gum and baling wire.
options synchprobs options synchprobs # Uncomment to enable ASST1 synchronization problems

View File

@ -309,6 +309,14 @@ file ../common/libc/string/strlen.c
file ../common/libc/string/strrchr.c file ../common/libc/string/strrchr.c
file ../common/libc/string/strtok_r.c file ../common/libc/string/strtok_r.c
#
# libtest161 shared code and security functions
#
file ../common/libtest161/test161.c
file ../common/libtest161/secure.c
file ../common/libtest161/sha256.c
######################################## ########################################
# # # #
# Core kernel source files # # Core kernel source files #
@ -437,12 +445,19 @@ file test/threadlisttest.c
file test/threadtest.c file test/threadtest.c
file test/tt3.c file test/tt3.c
file test/synchtest.c file test/synchtest.c
file test/rwtest.c
file test/semunit.c file test/semunit.c
file test/hmacunit.c
file test/kmalloctest.c file test/kmalloctest.c
file test/fstest.c file test/fstest.c
file test/lib.c
optfile net test/nettest.c optfile net test/nettest.c
defoption synchprobs defoption synchprobs
optfile synchprobs synchprobs/whalemating.c optfile synchprobs synchprobs/whalemating.c
optfile synchprobs synchprobs/stoplight.c optfile synchprobs synchprobs/stoplight.c
optfile synchprobs test/synchprobs.c optfile synchprobs test/synchprobs.c
defoption automationtest
optfile automationtest test/automationtest.c

View File

@ -208,7 +208,7 @@ echo "$CONFNAME" $CONFTMP | awk '
# #
if [ ! -d "$COMPILEDIR" ]; then if [ ! -d "$COMPILEDIR" ]; then
mkdir $COMPILEDIR mkdir -p $COMPILEDIR
fi fi
echo -n 'Generating files...' echo -n 'Generating files...'

View File

@ -35,6 +35,7 @@
#include <threadlist.h> #include <threadlist.h>
#include <machine/vm.h> /* for TLBSHOOTDOWN_MAX */ #include <machine/vm.h> /* for TLBSHOOTDOWN_MAX */
extern unsigned num_cpus;
/* /*
* Per-cpu structure * Per-cpu structure

View File

@ -0,0 +1,50 @@
/*
* NO NOT MODIFY THIS FILE
*
* All the contents of this file are overwritten during automated
* testing. Please consider this before changing anything in this file.
*/
/*
* Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2008, 2009
* The President and Fellows of Harvard College.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#ifndef _SECRET_H_
#define _SECRET_H_
/*
* During automated testing all instances of KERNEL_SECRET in trusted tests
* are rewritten to a random value to ensure that the kernel is actually
* running the appropriate tests. So this value is never actually used, but
* allows normally compilation and operation.
*/
#undef SECRET_TESTING
#define SECRET "SECRET"
#endif /* _SECRET_H_ */

View File

@ -0,0 +1,18 @@
#ifndef _KERN_SECURE_H_
#define _KERN_SECURE_H_
/*
* Compute the FIPS 198-1 complient HMAC of msg using SHA256.
*
* hmac_with_salt uses a salted key and sets salt_str to this value (in hex).
* Both functions below create hash_str with the hex readable version of the hash.
*
* Callers need to free hash_str (and salt_str) when done.
*/
int hmac(const char *msg, size_t msg_len, const char *key, size_t key_len,
char **hash_str);
int hmac_salted(const char *msg, size_t msg_len, const char *key, size_t key_len,
char **hash_str, char **salt_str);
#endif //_KERN_SECURE_H_

View File

@ -0,0 +1,76 @@
/*
* Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2008, 2009
* The President and Fellows of Harvard College.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#ifndef _KERN_TEST161_H_
#define _KERN_TEST161_H_
#define TEST161_SUCCESS 0
#define TEST161_FAIL 1
#include <kern/secret.h>
#ifdef _KERNEL
#define __TEST161_PROGRESS_N(iter, mod) do { \
if (((iter) % mod) == 0) { \
kprintf("."); \
} \
} while (0)
#else
#include <stdio.h>
#define __TEST161_PROGRESS_N(iter, mod) do { \
if (((iter) % mod) == 0) { \
printf("."); \
} \
} while (0)
#endif
// Always loud
#define TEST161_LPROGRESS_N(iter, mod) __TEST161_PROGRESS_N(iter, mod)
#define TEST161_LPROGRESS(iter) __TEST161_PROGRESS_N(iter, 100)
// Depends on whether or not it's automated testing. Some tests are
// quite verbose with useful information so these should just stay quiet.
#ifdef SECRET_TESTING
#define TEST161_TPROGRESS_N(iter, mod) __TEST161_PROGRESS_N(iter, mod)
#define TEST161_TPROGRESS(iter) __TEST161_PROGRESS_N(iter, 100)
#else
#define TEST161_TPROGRESS_N(iter, mod)
#define TEST161_TPROGRESS(iter)
#endif
int success(int, const char *, const char *);
int secprintf(const char *secret, const char *msg, const char *name);
int snsecprintf(size_t len, char *buffer, const char *secret, const char *msg, const char *name);
int partial_credit(const char *secret, const char *name, int scored, int total);
#ifdef _KERNEL
void test161_bootstrap(void);
#endif
#endif /* _KERN_TEST161_H_ */

View File

@ -129,6 +129,8 @@ uint32_t random(void);
void *kmalloc(size_t size); void *kmalloc(size_t size);
void kfree(void *ptr); void kfree(void *ptr);
void kheap_printstats(void); void kheap_printstats(void);
void kheap_printused(void);
unsigned long kheap_getused(void);
void kheap_nextgeneration(void); void kheap_nextgeneration(void);
void kheap_dump(void); void kheap_dump(void);
void kheap_dumpall(void); void kheap_dumpall(void);

47
kern/include/prompt.h Normal file
View File

@ -0,0 +1,47 @@
/*
* NO NOT MODIFY THIS FILE
*
* All the contents of this file are overwritten during automated
* testing. Please consider this before changing anything in this file.
*/
/*
* Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2008, 2009
* The President and Fellows of Harvard College.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#ifndef _PROMPT_H_
#define _PROMPT_H_
/*
* You can set a custom prompt here, but the default will be used during
* testing.
*/
#define KERNEL_PROMPT "OS/161 kernel [? for menu]: "
#endif /* _PROMPT_H_ */

View File

@ -92,9 +92,18 @@ void spllower(int oldipl, int newipl);
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
SPL_INLINE SPL_INLINE
int spl0(void) { return splx(IPL_NONE); } int
spl0(void)
{
return splx(IPL_NONE);
}
SPL_INLINE SPL_INLINE
int splhigh(void) { return splx(IPL_HIGH); } int
splhigh(void)
{
return splx(IPL_HIGH);
}
#endif /* _SPL_H_ */ #endif /* _SPL_H_ */

View File

@ -34,6 +34,7 @@
* Header file for synchronization primitives. * Header file for synchronization primitives.
*/ */
#include <spinlock.h> #include <spinlock.h>
/* /*
@ -61,6 +62,7 @@ void sem_destroy(struct semaphore *);
void P(struct semaphore *); void P(struct semaphore *);
void V(struct semaphore *); void V(struct semaphore *);
/* /*
* Simple lock for mutual exclusion. * Simple lock for mutual exclusion.
* *
@ -95,6 +97,7 @@ void lock_acquire(struct lock *);
void lock_release(struct lock *); void lock_release(struct lock *);
bool lock_do_i_hold(struct lock *); bool lock_do_i_hold(struct lock *);
/* /*
* Condition variable. * Condition variable.
* *
@ -135,4 +138,40 @@ void cv_wait(struct cv *cv, struct lock *lock);
void cv_signal(struct cv *cv, struct lock *lock); void cv_signal(struct cv *cv, struct lock *lock);
void cv_broadcast(struct cv *cv, struct lock *lock); void cv_broadcast(struct cv *cv, struct lock *lock);
/*
* Reader-writer locks.
*
* When the lock is created, no thread should be holding it. Likewise,
* when the lock is destroyed, no thread should be holding it.
*
* The name field is for easier debugging. A copy of the name is
* (should be) made internally.
*/
struct rwlock {
char *rwlock_name;
// add what you need here
// (don't forget to mark things volatile as needed)
};
struct rwlock * rwlock_create(const char *);
void rwlock_destroy(struct rwlock *);
/*
* Operations:
* rwlock_acquire_read - Get the lock for reading. Multiple threads can
* hold the lock for reading at the same time.
* rwlock_release_read - Free the lock.
* rwlock_acquire_write - Get the lock for writing. Only one thread can
* hold the write lock at one time.
* rwlock_release_write - Free the write lock.
*
* These operations must be atomic. You get to write them.
*/
void rwlock_acquire_read(struct rwlock *);
void rwlock_release_read(struct rwlock *);
void rwlock_acquire_write(struct rwlock *);
void rwlock_release_write(struct rwlock *);
#endif /* _SYNCH_H_ */ #endif /* _SYNCH_H_ */

View File

@ -30,6 +30,13 @@
#ifndef _TEST_H_ #ifndef _TEST_H_
#define _TEST_H_ #define _TEST_H_
/* Get __PF() for declaring printf-like functions. */
#include <cdefs.h>
#include <kern/secret.h>
#include "opt-synchprobs.h"
#include "opt-automationtest.h"
/* /*
* Declarations for test code and other miscellaneous high-level * Declarations for test code and other miscellaneous high-level
* functions. * functions.
@ -52,8 +59,20 @@ int threadtest2(int, char **);
int threadtest3(int, char **); int threadtest3(int, char **);
int semtest(int, char **); int semtest(int, char **);
int locktest(int, char **); int locktest(int, char **);
int locktest2(int, char **);
int locktest3(int, char **);
int locktest4(int, char **);
int locktest5(int, char **);
int cvtest(int, char **); int cvtest(int, char **);
int cvtest2(int, char **); int cvtest2(int, char **);
int cvtest3(int, char **);
int cvtest4(int, char **);
int cvtest5(int, char **);
int rwtest(int, char **);
int rwtest2(int, char **);
int rwtest3(int, char **);
int rwtest4(int, char **);
int rwtest5(int, char **);
/* semaphore unit tests */ /* semaphore unit tests */
int semu1(int, char **); int semu1(int, char **);
@ -88,11 +107,15 @@ int longstress(int, char **);
int createstress(int, char **); int createstress(int, char **);
int printfile(int, char **); int printfile(int, char **);
/* HMAC/hash tests */
int hmacu1(int, char**);
/* other tests */ /* other tests */
int kmalloctest(int, char **); int kmalloctest(int, char **);
int kmallocstress(int, char **); int kmallocstress(int, char **);
int kmalloctest3(int, char **); int kmalloctest3(int, char **);
int kmalloctest4(int, char **); int kmalloctest4(int, char **);
int kmalloctest5(int, char **);
int nettest(int, char **); int nettest(int, char **);
/* Routine for running a user-level program. */ /* Routine for running a user-level program. */
@ -104,5 +127,75 @@ void menu(char *argstr);
/* The main function, called from start.S. */ /* The main function, called from start.S. */
void kmain(char *bootstring); void kmain(char *bootstring);
#if OPT_SYNCHPROBS
/*
* Synchronization driver primitives.
*/
void male_start(uint32_t);
void male_end(uint32_t);
void female_start(uint32_t);
void female_end(uint32_t);
void matchmaker_start(uint32_t);
void matchmaker_end(uint32_t);
int whalemating(int, char **);
void inQuadrant(int, uint32_t);
void leaveIntersection(uint32_t);
int stoplight(int, char **);
/*
* Synchronization problem primitives.
*/
/*
* whalemating.c.
*/
void whalemating_init(void);
void whalemating_cleanup(void);
void male(uint32_t);
void female(uint32_t);
void matchmaker(uint32_t);
/*
* stoplight.c.
*/
void gostraight(uint32_t, uint32_t);
void turnleft(uint32_t, uint32_t);
void turnright(uint32_t, uint32_t);
void stoplight_init(void);
void stoplight_cleanup(void);
#endif
/*
* Automation tests for detecting kernel deadlocks and livelocks.
*/
#if OPT_AUTOMATIONTEST
int dltest(int, char **);
int ll1test(int, char **);
int ll16test(int, char **);
#endif
void random_yielder(uint32_t);
void random_spinner(uint32_t);
/*
* kprintf variants that do not (or only) print during automated testing.
*/
#ifdef SECRET_TESTING
#define kprintf_t(...) kprintf(__VA_ARGS__)
#define kprintf_n(...) silent(__VA_ARGS__)
#else
#define kprintf_t(...) silent(__VA_ARGS__)
#define kprintf_n(...) kprintf(__VA_ARGS__)
#endif
static inline void silent(const char * fmt, ...) { (void)fmt; };
#endif /* _TEST_H_ */ #endif /* _TEST_H_ */

View File

@ -48,6 +48,7 @@ struct cpu;
/* Size of kernel stacks; must be power of 2 */ /* Size of kernel stacks; must be power of 2 */
#define STACK_SIZE 4096 #define STACK_SIZE 4096
#define MAX_NAME_LENGTH 64
/* Mask for extracting the stack base address of a kernel stack pointer */ /* Mask for extracting the stack base address of a kernel stack pointer */
#define STACK_MASK (~(vaddr_t)(STACK_SIZE-1)) #define STACK_MASK (~(vaddr_t)(STACK_SIZE-1))
@ -70,7 +71,16 @@ struct thread {
* These go up front so they're easy to get to even if the * These go up front so they're easy to get to even if the
* debugger is messed up. * debugger is messed up.
*/ */
char *t_name; /* Name of this thread */
/*
* Name of this thread. Used to be dynamically allocated using kmalloc, but
* this can cause small changes in the amount of available memory due to the
* fact that it was cleaned up in exorcise. This produces more predictable
* behavior at the cost of a small amount of memory overhead and the
* inability to give threads huge names.
*/
char t_name[MAX_NAME_LENGTH];
const char *t_wchan_name; /* Name of wait channel, if sleeping */ const char *t_wchan_name; /* Name of wait channel, if sleeping */
threadstate_t t_state; /* State this thread is in */ threadstate_t t_state; /* State this thread is in */
@ -169,5 +179,7 @@ void schedule(void);
*/ */
void thread_consider_migration(void); void thread_consider_migration(void);
extern unsigned thread_count;
void thread_wait_for_count(unsigned);
#endif /* _THREAD_H_ */ #endif /* _THREAD_H_ */

View File

@ -55,6 +55,13 @@ int vm_fault(int faulttype, vaddr_t faultaddress);
vaddr_t alloc_kpages(unsigned npages); vaddr_t alloc_kpages(unsigned npages);
void free_kpages(vaddr_t addr); void free_kpages(vaddr_t addr);
/*
* Return amount of memory (in bytes) used by allocated coremap pages. If
* there are ongoing allocations, this value could change after it is returned
* to the caller. But it should have been correct at some point in time.
*/
unsigned int coremap_used_bytes(void);
/* TLB shootdown handling called from interprocessor_interrupt */ /* TLB shootdown handling called from interprocessor_interrupt */
void vm_tlbshootdown(const struct tlbshootdown *); void vm_tlbshootdown(const struct tlbshootdown *);

View File

@ -39,6 +39,9 @@
#include <mainbus.h> #include <mainbus.h>
#include <vfs.h> // for vfs_sync() #include <vfs.h> // for vfs_sync()
#include <lamebus/ltrace.h> // for ltrace_stop() #include <lamebus/ltrace.h> // for ltrace_stop()
#include <kern/secret.h>
#include <test.h>
/* Flags word for DEBUG() macro. */ /* Flags word for DEBUG() macro. */
uint32_t dbflags = 0; uint32_t dbflags = 0;
@ -49,16 +52,20 @@ static struct lock *kprintf_lock;
/* Lock for polled kprintfs */ /* Lock for polled kprintfs */
static struct spinlock kprintf_spinlock; static struct spinlock kprintf_spinlock;
/* /*
* Warning: all this has to work from interrupt handlers and when * Warning: all this has to work from interrupt handlers and when
* interrupts are disabled. * interrupts are disabled.
*/ */
/* /*
* Create the kprintf lock. Must be called before creating a second * Create the kprintf lock. Must be called before creating a second
* thread or enabling a second CPU. * thread or enabling a second CPU.
*/ */
void kprintf_bootstrap(void) { void
kprintf_bootstrap(void)
{
KASSERT(kprintf_lock == NULL); KASSERT(kprintf_lock == NULL);
kprintf_lock = lock_create("kprintf_lock"); kprintf_lock = lock_create("kprintf_lock");
@ -71,52 +78,78 @@ void kprintf_bootstrap(void) {
/* /*
* Send characters to the console. Backend for __printf. * Send characters to the console. Backend for __printf.
*/ */
static void console_send(void *junk, const char *data, size_t len) { static
void
console_send(void *junk, const char *data, size_t len)
{
size_t i; size_t i;
(void)junk; (void)junk;
for (i = 0; i < len; i++) { for (i=0; i<len; i++) {
putch(data[i]); putch(data[i]);
} }
} }
/* /*
* Printf to the console. * kprintf and tprintf helper function.
*/ */
int kprintf(const char *fmt, ...) { static
inline
int
__kprintf(const char *fmt, va_list ap)
{
int chars; int chars;
va_list ap;
bool dolock; bool dolock;
dolock = kprintf_lock != NULL && curthread->t_in_interrupt == false && dolock = kprintf_lock != NULL
curthread->t_curspl == 0 && curcpu->c_spinlocks == 0; && curthread->t_in_interrupt == false
&& curthread->t_curspl == 0
&& curcpu->c_spinlocks == 0;
if (dolock) { if (dolock) {
lock_acquire(kprintf_lock); lock_acquire(kprintf_lock);
} else { }
else {
spinlock_acquire(&kprintf_spinlock); spinlock_acquire(&kprintf_spinlock);
} }
va_start(ap, fmt);
chars = __vprintf(console_send, NULL, fmt, ap); chars = __vprintf(console_send, NULL, fmt, ap);
va_end(ap);
if (dolock) { if (dolock) {
lock_release(kprintf_lock); lock_release(kprintf_lock);
} else { }
else {
spinlock_release(&kprintf_spinlock); spinlock_release(&kprintf_spinlock);
} }
return chars; return chars;
} }
/*
* Printf to the console.
*/
int
kprintf(const char *fmt, ...)
{
int chars;
va_list ap;
va_start(ap, fmt);
chars = __kprintf(fmt, ap);
va_end(ap);
return chars;
}
/* /*
* panic() is for fatal errors. It prints the printf arguments it's * panic() is for fatal errors. It prints the printf arguments it's
* passed and then halts the system. * passed and then halts the system.
*/ */
void panic(const char *fmt, ...) { void
panic(const char *fmt, ...)
{
va_list ap; va_list ap;
/* /*
@ -186,13 +219,15 @@ void panic(const char *fmt, ...) {
* Last resort, just in case. * Last resort, just in case.
*/ */
for (;;) for (;;);
;
} }
/* /*
* Assertion failures go through this. * Assertion failures go through this.
*/ */
void badassert(const char *expr, const char *file, int line, const char *func) { void
panic("Assertion failed: %s, at %s:%d (%s)\n", expr, file, line, func); badassert(const char *expr, const char *file, int line, const char *func)
{
panic("Assertion failed: %s, at %s:%d (%s)\n",
expr, file, line, func);
} }

View File

@ -48,9 +48,11 @@
#include <device.h> #include <device.h>
#include <syscall.h> #include <syscall.h>
#include <test.h> #include <test.h>
#include <kern/test161.h>
#include <version.h> #include <version.h>
#include "autoconf.h" // for pseudoconfig #include "autoconf.h" // for pseudoconfig
/* /*
* These two pieces of data are maintained by the makefiles and build system. * These two pieces of data are maintained by the makefiles and build system.
* buildconfig is the name of the config file the kernel was configured with. * buildconfig is the name of the config file the kernel was configured with.
@ -70,10 +72,14 @@ static const char harvard_copyright[] =
"Copyright (c) 2000, 2001-2005, 2008-2011, 2013, 2014\n" "Copyright (c) 2000, 2001-2005, 2008-2011, 2013, 2014\n"
" President and Fellows of Harvard College. All rights reserved.\n"; " President and Fellows of Harvard College. All rights reserved.\n";
/* /*
* Initial boot sequence. * Initial boot sequence.
*/ */
static void boot(void) { static
void
boot(void)
{
/* /*
* The order of these is important! * The order of these is important!
* Don't go changing it without thinking about the consequences. * Don't go changing it without thinking about the consequences.
@ -96,8 +102,8 @@ static void boot(void) {
kprintf("%s", harvard_copyright); kprintf("%s", harvard_copyright);
kprintf("\n"); kprintf("\n");
kprintf("Minh Tran's system version %s (%s #%d)\n", GROUP_VERSION, kprintf("Put-your-group-name-here's system version %s (%s #%d)\n",
buildconfig, buildversion); GROUP_VERSION, buildconfig, buildversion);
kprintf("\n"); kprintf("\n");
/* Early initialization. */ /* Early initialization. */
@ -122,6 +128,7 @@ static void boot(void) {
vm_bootstrap(); vm_bootstrap();
kprintf_bootstrap(); kprintf_bootstrap();
thread_start_cpus(); thread_start_cpus();
test161_bootstrap();
/* Default bootfs - but ignore failure, in case emu0 doesn't exist */ /* Default bootfs - but ignore failure, in case emu0 doesn't exist */
vfs_setbootfs("emu0"); vfs_setbootfs("emu0");
@ -138,7 +145,10 @@ static void boot(void) {
/* /*
* Shutdown sequence. Opposite to boot(). * Shutdown sequence. Opposite to boot().
*/ */
static void shutdown(void) { static
void
shutdown(void)
{
kprintf("Shutting down.\n"); kprintf("Shutting down.\n");
@ -160,7 +170,9 @@ static void shutdown(void) {
* not because this is where system call code should go. Other syscall * not because this is where system call code should go. Other syscall
* code should probably live in the "syscall" directory. * code should probably live in the "syscall" directory.
*/ */
int sys_reboot(int code) { int
sys_reboot(int code)
{
switch (code) { switch (code) {
case RB_REBOOT: case RB_REBOOT:
case RB_HALT: case RB_HALT:
@ -195,7 +207,9 @@ int sys_reboot(int code) {
* Kernel main. Boot up, then fork the menu thread; wait for a reboot * Kernel main. Boot up, then fork the menu thread; wait for a reboot
* request, and then shut down. * request, and then shut down.
*/ */
void kmain(char *arguments) { void
kmain(char *arguments)
{
boot(); boot();
menu(arguments); menu(arguments);

View File

@ -43,8 +43,11 @@
#include <sfs.h> #include <sfs.h>
#include <syscall.h> #include <syscall.h>
#include <test.h> #include <test.h>
#include <prompt.h>
#include "opt-sfs.h" #include "opt-sfs.h"
#include "opt-net.h" #include "opt-net.h"
#include "opt-synchprobs.h"
#include "opt-automationtest.h"
/* /*
* In-kernel menu and command dispatcher. * In-kernel menu and command dispatcher.
@ -116,6 +119,7 @@ common_prog(int nargs, char **args)
{ {
struct proc *proc; struct proc *proc;
int result; int result;
unsigned tc;
/* Create a process for the new program to run in. */ /* Create a process for the new program to run in. */
proc = proc_create_runprogram(args[0] /* name */); proc = proc_create_runprogram(args[0] /* name */);
@ -123,6 +127,8 @@ common_prog(int nargs, char **args)
return ENOMEM; return ENOMEM;
} }
tc = thread_count;
result = thread_fork(args[0] /* thread name */, result = thread_fork(args[0] /* thread name */,
proc /* new process */, proc /* new process */,
cmd_progthread /* thread function */, cmd_progthread /* thread function */,
@ -138,6 +144,10 @@ common_prog(int nargs, char **args)
* once you write the code for handling that. * once you write the code for handling that.
*/ */
// Wait for all threads to finish cleanup, otherwise khu be a bit behind,
// especially once swapping is enabled.
thread_wait_for_count(tc);
return 0; return 0;
} }
@ -465,6 +475,18 @@ cmd_kheapstats(int nargs, char **args)
return 0; return 0;
} }
static
int
cmd_kheapused(int nargs, char **args)
{
(void)nargs;
(void)args;
kheap_printused();
return 0;
}
static static
int int
cmd_kheapgeneration(int nargs, char **args) cmd_kheapgeneration(int nargs, char **args)
@ -560,16 +582,33 @@ static const char *testmenu[] = {
"[km2] kmalloc stress test ", "[km2] kmalloc stress test ",
"[km3] Large kmalloc test ", "[km3] Large kmalloc test ",
"[km4] Multipage kmalloc test ", "[km4] Multipage kmalloc test ",
"[km5] kmalloc coremap alloc test ",
"[tt1] Thread test 1 ", "[tt1] Thread test 1 ",
"[tt2] Thread test 2 ", "[tt2] Thread test 2 ",
"[tt3] Thread test 3 ", "[tt3] Thread test 3 ",
#if OPT_NET #if OPT_NET
"[net] Network test ", "[net] Network test ",
#endif #endif
"[sy1] Semaphore test ", "[sem1] Semaphore test ",
"[sy2] Lock test (1) ", "[lt1] Lock test 1 (1) ",
"[sy3] CV test (1) ", "[lt2] Lock test 2 (1*) ",
"[sy4] CV test #2 (1) ", "[lt3] Lock test 3 (1*) ",
"[lt4] Lock test 4 (1*) ",
"[lt5] Lock test 5 (1*) ",
"[cvt1] CV test 1 (1) ",
"[cvt2] CV test 2 (1) ",
"[cvt3] CV test 3 (1*) ",
"[cvt4] CV test 4 (1*) ",
"[cvt5] CV test 5 (1) ",
"[rwt1] RW lock test (1?) ",
"[rwt2] RW lock test 2 (1?) ",
"[rwt3] RW lock test 3 (1?) ",
"[rwt4] RW lock test 4 (1?) ",
"[rwt5] RW lock test 5 (1?) ",
#if OPT_SYNCHPROBS
"[sp1] Whalemating test (1) ",
"[sp2] Stoplight test (1) ",
#endif
"[semu1-22] Semaphore unit tests ", "[semu1-22] Semaphore unit tests ",
"[fs1] Filesystem test ", "[fs1] Filesystem test ",
"[fs2] FS read stress ", "[fs2] FS read stress ",
@ -577,6 +616,7 @@ static const char *testmenu[] = {
"[fs4] FS write stress 2 ", "[fs4] FS write stress 2 ",
"[fs5] FS long stress ", "[fs5] FS long stress ",
"[fs6] FS create stress ", "[fs6] FS create stress ",
"[hm1] HMAC unit test ",
NULL NULL
}; };
@ -590,15 +630,41 @@ cmd_testmenu(int n, char **a)
showmenu("OS/161 tests menu", testmenu); showmenu("OS/161 tests menu", testmenu);
kprintf(" (1) These tests will fail until you finish the " kprintf(" (1) These tests will fail until you finish the "
"synch assignment.\n"); "synch assignment.\n");
kprintf(" (*) These tests will panic on success.\n");
kprintf(" (?) These tests are left to you to implement.\n");
kprintf("\n"); kprintf("\n");
return 0; return 0;
} }
#if OPT_AUTOMATIONTEST
static const char *automationmenu[] = {
"[dl] Deadlock test (*) ",
"[ll1] Livelock test (1 thread) ",
"[ll16] Livelock test (16 threads) ",
NULL
};
static
int
cmd_automationmenu(int n, char **a)
{
(void)n;
(void)a;
showmenu("OS/161 automation tests menu", automationmenu);
kprintf(" (*) These tests require locks.\n");
kprintf("\n");
return 0;
}
#endif
static const char *mainmenu[] = { static const char *mainmenu[] = {
"[?o] Operations menu ", "[?o] Operations menu ",
"[?t] Tests menu ", "[?t] Tests menu ",
"[kh] Kernel heap stats ", "[kh] Kernel heap stats ",
"[khu] Kernel heap usage ",
"[khgen] Next kernel heap generation ", "[khgen] Next kernel heap generation ",
"[khdump] Dump kernel heap ", "[khdump] Dump kernel heap ",
"[q] Quit and shut down ", "[q] Quit and shut down ",
@ -630,6 +696,9 @@ static struct {
{ "help", cmd_mainmenu }, { "help", cmd_mainmenu },
{ "?o", cmd_opsmenu }, { "?o", cmd_opsmenu },
{ "?t", cmd_testmenu }, { "?t", cmd_testmenu },
#if OPT_AUTOMATIONTEST
{ "?a", cmd_automationmenu },
#endif
/* operations */ /* operations */
{ "s", cmd_shell }, { "s", cmd_shell },
@ -650,6 +719,7 @@ static struct {
/* stats */ /* stats */
{ "kh", cmd_kheapstats }, { "kh", cmd_kheapstats },
{ "khu", cmd_kheapused },
{ "khgen", cmd_kheapgeneration }, { "khgen", cmd_kheapgeneration },
{ "khdump", cmd_kheapdump }, { "khdump", cmd_kheapdump },
@ -662,18 +732,35 @@ static struct {
{ "km2", kmallocstress }, { "km2", kmallocstress },
{ "km3", kmalloctest3 }, { "km3", kmalloctest3 },
{ "km4", kmalloctest4 }, { "km4", kmalloctest4 },
{ "km5", kmalloctest5 },
#if OPT_NET #if OPT_NET
{ "net", nettest }, { "net", nettest },
#endif #endif
{ "tt1", threadtest }, { "tt1", threadtest },
{ "tt2", threadtest2 }, { "tt2", threadtest2 },
{ "tt3", threadtest3 }, { "tt3", threadtest3 },
{ "sy1", semtest },
/* synchronization assignment tests */ /* synchronization assignment tests */
{ "sy2", locktest }, { "sem1", semtest },
{ "sy3", cvtest }, { "lt1", locktest },
{ "sy4", cvtest2 }, { "lt2", locktest2 },
{ "lt3", locktest3 },
{ "lt4", locktest4 },
{ "lt5", locktest5 },
{ "cvt1", cvtest },
{ "cvt2", cvtest2 },
{ "cvt3", cvtest3 },
{ "cvt4", cvtest4 },
{ "cvt5", cvtest5 },
{ "rwt1", rwtest },
{ "rwt2", rwtest2 },
{ "rwt3", rwtest3 },
{ "rwt4", rwtest4 },
{ "rwt5", rwtest5 },
#if OPT_SYNCHPROBS
{ "sp1", whalemating },
{ "sp2", stoplight },
#endif
/* semaphore unit tests */ /* semaphore unit tests */
{ "semu1", semu1 }, { "semu1", semu1 },
@ -707,6 +794,16 @@ static struct {
{ "fs5", longstress }, { "fs5", longstress },
{ "fs6", createstress }, { "fs6", createstress },
/* HMAC unit tests */
{ "hm1", hmacu1 },
#if OPT_AUTOMATIONTEST
/* automation tests */
{ "dl", dltest },
{ "ll1", ll1test },
{ "ll16", ll16test },
#endif
{ NULL, NULL } { NULL, NULL }
}; };
@ -820,7 +917,11 @@ menu(char *args)
menu_execute(args, 1); menu_execute(args, 1);
while (1) { while (1) {
kprintf("OS/161 kernel [? for menu]: "); /*
* Defined in overwrite.h. If you want to change the kernel prompt, please
* do it in that file. Otherwise automated test testing will break.
*/
kprintf(KERNEL_PROMPT);
kgets(buf, sizeof(buf)); kgets(buf, sizeof(buf));
menu_execute(buf, 0); menu_execute(buf, 0);
} }

118
kern/synchprobs/stoplight.c Normal file
View File

@ -0,0 +1,118 @@
/*
* Copyright (c) 2001, 2002, 2009
* The President and Fellows of Harvard College.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/*
* Driver code is in kern/tests/synchprobs.c We will replace that file. This
* file is yours to modify as you see fit.
*
* You should implement your solution to the stoplight problem below. The
* quadrant and direction mappings for reference: (although the problem is, of
* course, stable under rotation)
*
* |0 |
* - --
* 01 1
* 3 32
* -- --
* | 2|
*
* As way to think about it, assuming cars drive on the right: a car entering
* the intersection from direction X will enter intersection quadrant X first.
* The semantics of the problem are that once a car enters any quadrant it has
* to be somewhere in the intersection until it call leaveIntersection(),
* which it should call while in the final quadrant.
*
* As an example, let's say a car approaches the intersection and needs to
* pass through quadrants 0, 3 and 2. Once you call inQuadrant(0), the car is
* considered in quadrant 0 until you call inQuadrant(3). After you call
* inQuadrant(2), the car is considered in quadrant 2 until you call
* leaveIntersection().
*
* You will probably want to write some helper functions to assist with the
* mappings. Modular arithmetic can help, e.g. a car passing straight through
* the intersection entering from direction X will leave to direction (X + 2)
* % 4 and pass through quadrants X and (X + 3) % 4. Boo-yah.
*
* Your solutions below should call the inQuadrant() and leaveIntersection()
* functions in synchprobs.c to record their progress.
*/
#include <types.h>
#include <lib.h>
#include <thread.h>
#include <test.h>
#include <synch.h>
/*
* Called by the driver during initialization.
*/
void
stoplight_init() {
return;
}
/*
* Called by the driver during teardown.
*/
void stoplight_cleanup() {
return;
}
void
turnright(uint32_t direction, uint32_t index)
{
(void)direction;
(void)index;
/*
* Implement this function.
*/
return;
}
void
gostraight(uint32_t direction, uint32_t index)
{
(void)direction;
(void)index;
/*
* Implement this function.
*/
return;
}
void
turnleft(uint32_t direction, uint32_t index)
{
(void)direction;
(void)index;
/*
* Implement this function.
*/
return;
}

View File

@ -0,0 +1,91 @@
/*
* Copyright (c) 2001, 2002, 2009
* The President and Fellows of Harvard College.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/*
* Driver code is in kern/tests/synchprobs.c We will
* replace that file. This file is yours to modify as you see fit.
*
* You should implement your solution to the whalemating problem below.
*/
#include <types.h>
#include <lib.h>
#include <thread.h>
#include <test.h>
#include <synch.h>
/*
* Called by the driver during initialization.
*/
void whalemating_init() {
return;
}
/*
* Called by the driver during teardown.
*/
void
whalemating_cleanup() {
return;
}
void
male(uint32_t index)
{
(void)index;
/*
* Implement this function by calling male_start and male_end when
* appropriate.
*/
return;
}
void
female(uint32_t index)
{
(void)index;
/*
* Implement this function by calling female_start and female_end when
* appropriate.
*/
return;
}
void
matchmaker(uint32_t index)
{
(void)index;
/*
* Implement this function by calling matchmaker_start and matchmaker_end
* when appropriate.
*/
return;
}

175
kern/test/automationtest.c Normal file
View File

@ -0,0 +1,175 @@
/*
* Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2008, 2009
* The President and Fellows of Harvard College.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/*
* Automation test code for creating (and detecting) kernel dead and livelocks.
*/
#include <types.h>
#include <kern/wait.h>
#include <lib.h>
#include <clock.h>
#include <thread.h>
#include <synch.h>
#include <test.h>
#include <kern/secret.h>
#include <spinlock.h>
#define MAX_SPINNERS 16
static struct lock *deadlock_locks[2];
static struct semaphore *deadlock_sem;
struct spinlock spinners_lock[MAX_SPINNERS];
static
void
inititems(void)
{
int i;
for (i = 0; i < 2; i++) {
deadlock_locks[i] = lock_create("deadlock lock");
if (deadlock_locks[i] == NULL) {
panic("automationtest: lock_create failed\n");
}
}
deadlock_sem = sem_create("deadlock sem", 0);
if (deadlock_sem == NULL) {
panic("automationtest: sem_create failed\n");
}
for (i = 0; i < MAX_SPINNERS; i++) {
spinlock_init(&(spinners_lock[i]));
}
}
static
void
dltestthread(void *junk1, unsigned long junk2)
{
(void)junk1;
(void)junk2;
lock_acquire(deadlock_locks[1]);
V(deadlock_sem);
lock_acquire(deadlock_locks[0]);
}
int
dltest(int nargs, char **args)
{
int result;
(void)nargs;
(void)args;
inititems();
lock_acquire(deadlock_locks[0]);
result = thread_fork("dltest", NULL, dltestthread, NULL, (unsigned long)0);
if (result) {
panic("dltest: thread_fork failed: %s\n", strerror(result));
}
P(deadlock_sem);
lock_acquire(deadlock_locks[1]);
panic("dltest: didn't create deadlock (locks probably don't work)\n");
// 09 Jan 2015 : GWA : Shouldn't return.
return 0;
}
inline
static
void
infinite_spinner(unsigned long i)
{
(void)i;
volatile int j;
for (j=0; j<10000000; j++);
spinlock_acquire(&(spinners_lock[i]));
for (j=0; j<=1000; j++) {
if (j == 1000) {
j = 0;
}
}
panic("ll1test: infinite spin loop completed\n");
}
int
ll1test(int nargs, char **args)
{
(void)nargs;
(void)args;
inititems();
infinite_spinner((unsigned long) 0);
// 09 Jan 2015 : GWA : Shouldn't return.
return 0;
}
static
void
ll16testthread(void *junk1, unsigned long i)
{
(void)junk1;
infinite_spinner(i);
}
int
ll16test(int nargs, char **args)
{
int i, result;
inititems();
(void)nargs;
(void)args;
for (i=1; i<16; i++) {
result = thread_fork("ll16testthread", NULL, ll16testthread, NULL, (unsigned long)i);
if (result) {
panic("ll16test: thread_fork failed: %s\n", strerror(result));
}
}
infinite_spinner(0);
// 09 Jan 2015 : GWA : Shouldn't return.
return 0;
}

99
kern/test/hmacunit.c Normal file
View File

@ -0,0 +1,99 @@
/*
* Copyright (c) 2015
* The President and Fellows of Harvard College.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <types.h>
#include <lib.h>
#include <spinlock.h>
#include <synch.h>
#include <thread.h>
#include <current.h>
#include <clock.h>
#include <test.h>
#include <kern/secure.h>
/*
* Unit tests for hmac/sha256 hashing.
*/
////////////////////////////////////////////////////////////
// support code
static
void
ok(void)
{
kprintf("Test passed.\n");
}
/*
* Unit test 1
*
* Test some known msg/key/hashes to make sure we produce the
* right results.
*/
static const char *plaintext1[] = {
"The quick brown fox jumps over the lazy dog",
"The only people for me are the mad ones",
"I don't exactly know what I mean by that, but I mean it.",
};
static const char *keys1[] = {
"xqWmgzbvGuLIeeKOrwMA",
"ZxuvolLXL7C68pDjsclX",
"PYeuVzKuB03awYDgJotS",
};
static const char *hashes1[] = {
"251ab1da03c94435daf44898fcd11606669e222270e4ac90d04a18a9df8fdfd6",
"75bbf48c53ccba08c244447ef7eff2e0a02f23acfdac6502282ec431823fb393",
"6d7d2b5eabcda504f26de7547185483b19f9953a6eaeec6c364bb45e20b28598",
};
#define N_TESTS_1 3
int
hmacu1(int nargs, char **args)
{
char *hash;
int res;
(void)nargs; (void)args;
int i;
for (i = 0; i < N_TESTS_1; i++)
{
res = hmac(plaintext1[i], strlen(plaintext1[i]), keys1[i], strlen(keys1[i]), &hash);
KASSERT(!res);
KASSERT(strcmp(hash, hashes1[i]) == 0);
kfree(hash);
}
ok();
/* clean up */
return 0;
}

View File

@ -33,13 +33,19 @@
#include <types.h> #include <types.h>
#include <kern/errno.h> #include <kern/errno.h>
#include <lib.h> #include <lib.h>
#include <cpu.h>
#include <thread.h> #include <thread.h>
#include <synch.h> #include <synch.h>
#include <vm.h> /* for PAGE_SIZE */ #include <vm.h> /* for PAGE_SIZE */
#include <test.h> #include <test.h>
#include <kern/test161.h>
#include <mainbus.h>
#include "opt-dumbvm.h" #include "opt-dumbvm.h"
// from arch/mips/vm/ram.c
extern vaddr_t firstfree;
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
// km1/km2 // km1/km2
@ -58,6 +64,12 @@
#define ITEMSIZE 997 #define ITEMSIZE 997
#define NTHREADS 8 #define NTHREADS 8
#define PROGRESS(iter) do { \
if ((iter % 100) == 0) { \
kprintf("."); \
} \
} while (0)
static static
void void
kmallocthread(void *sm, unsigned long num) kmallocthread(void *sm, unsigned long num)
@ -69,15 +81,16 @@ kmallocthread(void *sm, unsigned long num)
int i; int i;
for (i=0; i<NTRIES; i++) { for (i=0; i<NTRIES; i++) {
PROGRESS(i);
ptr = kmalloc(ITEMSIZE); ptr = kmalloc(ITEMSIZE);
if (ptr==NULL) { if (ptr==NULL) {
if (sem) { if (sem) {
kprintf("thread %lu: kmalloc returned NULL\n", kprintf("thread %lu: kmalloc returned NULL\n",
num); num);
goto done; panic("kmalloc test failed");
} }
kprintf("kmalloc returned null; test failed.\n"); kprintf("kmalloc returned null; test failed.\n");
goto done; panic("kmalloc test failed");
} }
if (oldptr2) { if (oldptr2) {
kfree(oldptr2); kfree(oldptr2);
@ -85,7 +98,7 @@ kmallocthread(void *sm, unsigned long num)
oldptr2 = oldptr; oldptr2 = oldptr;
oldptr = ptr; oldptr = ptr;
} }
done:
if (oldptr2) { if (oldptr2) {
kfree(oldptr2); kfree(oldptr2);
} }
@ -105,7 +118,8 @@ kmalloctest(int nargs, char **args)
kprintf("Starting kmalloc test...\n"); kprintf("Starting kmalloc test...\n");
kmallocthread(NULL, 0); kmallocthread(NULL, 0);
kprintf("kmalloc test done\n"); kprintf("\n");
success(TEST161_SUCCESS, SECRET, "km1");
return 0; return 0;
} }
@ -140,7 +154,8 @@ kmallocstress(int nargs, char **args)
} }
sem_destroy(sem); sem_destroy(sem);
kprintf("kmalloc stress test done\n"); kprintf("\n");
success(TEST161_SUCCESS, SECRET, "km2");
return 0; return 0;
} }
@ -252,6 +267,7 @@ kmalloctest3(int nargs, char **args)
curpos = 0; curpos = 0;
cursizeindex = 0; cursizeindex = 0;
for (i=0; i<numptrs; i++) { for (i=0; i<numptrs; i++) {
PROGRESS(i);
cursize = sizes[cursizeindex]; cursize = sizes[cursizeindex];
ptr = ptrblocks[curblock][curpos]; ptr = ptrblocks[curblock][curpos];
KASSERT(ptr != NULL); KASSERT(ptr != NULL);
@ -282,13 +298,15 @@ kmalloctest3(int nargs, char **args)
/* Free the lower tier. */ /* Free the lower tier. */
for (i=0; i<numptrblocks; i++) { for (i=0; i<numptrblocks; i++) {
PROGRESS(i);
KASSERT(ptrblocks[i] != NULL); KASSERT(ptrblocks[i] != NULL);
kfree(ptrblocks[i]); kfree(ptrblocks[i]);
} }
/* Free the upper tier. */ /* Free the upper tier. */
kfree(ptrblocks); kfree(ptrblocks);
kprintf("kmalloctest3: passed\n"); kprintf("\n");
success(TEST161_SUCCESS, SECRET, "km3");
return 0; return 0;
} }
@ -300,20 +318,24 @@ void
kmalloctest4thread(void *sm, unsigned long num) kmalloctest4thread(void *sm, unsigned long num)
{ {
#define NUM_KM4_SIZES 5 #define NUM_KM4_SIZES 5
#define ITERATIONS 50
static const unsigned sizes[NUM_KM4_SIZES] = { 1, 3, 5, 2, 4 }; static const unsigned sizes[NUM_KM4_SIZES] = { 1, 3, 5, 2, 4 };
struct semaphore *sem = sm; struct semaphore *sem = sm;
void *ptrs[NUM_KM4_SIZES]; void *ptrs[NUM_KM4_SIZES];
unsigned p, q; unsigned p, q;
unsigned i; unsigned i, j, k;
uint32_t magic;
for (i=0; i<NUM_KM4_SIZES; i++) { for (i=0; i<NUM_KM4_SIZES; i++) {
ptrs[i] = NULL; ptrs[i] = NULL;
} }
p = 0; p = 0;
q = NUM_KM4_SIZES / 2; q = NUM_KM4_SIZES / 2;
magic = random();
for (i=0; i<NTRIES; i++) { for (i=0; i<NTRIES; i++) {
PROGRESS(i);
if (ptrs[q] != NULL) { if (ptrs[q] != NULL) {
kfree(ptrs[q]); kfree(ptrs[q]);
ptrs[q] = NULL; ptrs[q] = NULL;
@ -324,6 +346,24 @@ kmalloctest4thread(void *sm, unsigned long num)
"allocating %u pages failed\n", "allocating %u pages failed\n",
num, sizes[p]); num, sizes[p]);
} }
// Write to each page of the allocated memory and make sure nothing
// overwrites it.
for (k = 0; k < sizes[p]; k++) {
*((uint32_t *)ptrs[p] + k*PAGE_SIZE/sizeof(uint32_t)) = magic;
}
for (j = 0; j < ITERATIONS; j++) {
random_yielder(4);
for (k = 0; k < sizes[p]; k++) {
uint32_t actual = *((uint32_t *)ptrs[p] + k*PAGE_SIZE/sizeof(uint32_t));
if (actual != magic) {
panic("km4: expected %u got %u. Your VM is broken!",
magic, actual);
}
}
}
magic++;
p = (p + 1) % NUM_KM4_SIZES; p = (p + 1) % NUM_KM4_SIZES;
q = (q + 1) % NUM_KM4_SIZES; q = (q + 1) % NUM_KM4_SIZES;
} }
@ -375,6 +415,193 @@ kmalloctest4(int nargs, char **args)
} }
sem_destroy(sem); sem_destroy(sem);
kprintf("Multipage kmalloc test done\n");
kprintf("\n");
success(TEST161_SUCCESS, SECRET, "km4");
return 0;
}
static inline
void
km5_usage()
{
kprintf("usage: km5 [--avail <num_pages>] [--kernel <num_pages>]\n");
}
/*
* Allocate and free all physical memory a number of times. Along the we, we
* check coremap_used_bytes to make sure it's reporting the number we're
* expecting.
*/
int
kmalloctest5(int nargs, char **args)
{
#define KM5_ITERATIONS 5
// We're expecting an even number of arguments, less arg[0].
if (nargs > 5 || (nargs % 2) == 0) {
km5_usage();
return 0;
}
unsigned avail_page_slack = 0, kernel_page_limit = 0;
int arg = 1;
while (arg < nargs) {
if (strcmp(args[arg], "--avail") == 0) {
arg++;
avail_page_slack = atoi(args[arg++]);
} else if (strcmp(args[arg], "--kernel") == 0) {
arg++;
kernel_page_limit = atoi(args[arg++]);
} else {
km5_usage();
return 0;
}
}
#if OPT_DUMBVM
kprintf("(This test will not work with dumbvm)\n");
#endif
// First, we need to figure out how much memory we're running with and how
// much space it will take up if we maintain a pointer to each allocated
// page. We do something similar to km3 - for 32 bit systems with
// PAGE_SIZE == 4096, we can store 1024 pointers on a page. We keep an array
// of page size blocks of pointers which in total can hold enough pointers
// for each page of available physical memory.
unsigned orig_used, ptrs_per_page, num_ptr_blocks, max_pages;
unsigned total_ram, avail_ram, magic, orig_magic, known_pages;
ptrs_per_page = PAGE_SIZE / sizeof(void *);
total_ram = mainbus_ramsize();
avail_ram = total_ram - (uint32_t)(firstfree - MIPS_KSEG0);
max_pages = (avail_ram + PAGE_SIZE-1) / PAGE_SIZE;
num_ptr_blocks = (max_pages + ptrs_per_page-1) / ptrs_per_page;
// The array can go on the stack, we won't have that many
// (sys161 16M max => 4 blocks)
void **ptrs[num_ptr_blocks];
for (unsigned i = 0; i < num_ptr_blocks; i++) {
ptrs[i] = kmalloc(PAGE_SIZE);
if (ptrs[i] == NULL) {
panic("Can't allocate ptr page!");
}
bzero(ptrs[i], PAGE_SIZE);
}
kprintf("km5 --> phys ram: %uk avail ram: %uk (%u pages) ptr blocks: %u\n", total_ram/1024,
avail_ram/1024, max_pages, num_ptr_blocks);
// Initially, there must be at least 1 page allocated for each thread stack,
// one page for kmalloc for this thread struct, plus what we just allocated).
// This probably isn't the GLB, but its a decent lower bound.
orig_used = coremap_used_bytes();
known_pages = num_cpus + num_ptr_blocks + 1;
if (orig_used < known_pages * PAGE_SIZE) {
panic ("Not enough pages initially allocated");
}
if ((orig_used % PAGE_SIZE) != 0) {
panic("Coremap used bytes should be a multiple of PAGE_SIZE");
}
// Test for kernel bloat.
if (kernel_page_limit > 0) {
uint32_t kpages = (total_ram - avail_ram + PAGE_SIZE) / PAGE_SIZE;
if (kpages > kernel_page_limit) {
panic("You're kernel is bloated! Max allowed pages: %d, used pages: %d",
kernel_page_limit, kpages);
}
}
orig_magic = magic = random();
for (int i = 0; i < KM5_ITERATIONS; i++) {
// Step 1: allocate all physical memory, with checks along the way
unsigned int block, pos, oom, pages, used, prev;
void *page;
block = pos = oom = pages = used = 0;
prev = orig_used;
while (pages < max_pages+1) {
PROGRESS(pages);
page = kmalloc(PAGE_SIZE);
if (page == NULL) {
oom = 1;
break;
}
// Make sure we can write to the page
*(uint32_t *)page = magic++;
// Make sure the number of used bytes is going up, and by increments of PAGE_SIZE
used = coremap_used_bytes();
if (used != prev + PAGE_SIZE) {
panic("Allocation not equal to PAGE_SIZE. prev: %u used: %u", prev, used);
}
prev = used;
ptrs[block][pos] = page;
pos++;
if (pos >= ptrs_per_page) {
pos = 0;
block++;
}
pages++;
}
// Step 2: Check that we were able to allocate a reasonable number of pages
unsigned expected;
if (avail_page_slack > 0 ) {
// max avail pages + what we can prove we allocated + some slack
expected = max_pages - (known_pages + avail_page_slack);
} else {
// At the very least, just so we know things are working.
expected = 3;
}
if (pages < expected) {
panic("Expected to allocate at least %d pages, only allocated %d",
expected, pages);
}
// We tried to allocate 1 more page than is available in physical memory. That
// should fail unless you're swapping out kernel pages, which you should
// probably not be doing.
if (!oom) {
panic("Allocated more pages than physical memory. Are you swapping kernel pages?");
}
// Step 3: free everything and check that we're back to where we started
for (block = 0; block < num_ptr_blocks; block++) {
for (pos = 0; pos < ptrs_per_page; pos++) {
if (ptrs[block][pos] != NULL) {
// Make sure we got unique addresses
if ((*(uint32_t *)ptrs[block][pos]) != orig_magic++) {
panic("km5: expected %u got %u - your VM is broken!",
orig_magic-1, (*(uint32_t *)ptrs[block][pos]));
}
kfree(ptrs[block][pos]);
}
}
}
// Check that we're back to where we started
used = coremap_used_bytes();
if (used != orig_used) {
panic("orig (%u) != used (%u)", orig_used, used);
}
}
//Clean up the pointer blocks
for (unsigned i = 0; i < num_ptr_blocks; i++) {
kfree(ptrs[i]);
}
kprintf("\n");
success(TEST161_SUCCESS, SECRET, "km5");
return 0; return 0;
} }

28
kern/test/lib.c Normal file
View File

@ -0,0 +1,28 @@
#include <types.h>
#include <thread.h>
#include <test.h>
#include <lib.h>
/*
* Helper functions used by testing and problem driver code
* to establish better mixtures of threads.
*/
void
random_yielder(uint32_t max_yield_count)
{
uint32_t i;
for (i = 0; i < random() % max_yield_count; i++) {
thread_yield();
}
}
void
random_spinner(uint32_t max_spin_count)
{
uint32_t i;
volatile int spin;
for (i = 0; i < random() % max_spin_count; i++) {
spin += i;
}
}

67
kern/test/rwtest.c Normal file
View File

@ -0,0 +1,67 @@
/*
* All the contents of this file are overwritten during automated
* testing. Please consider this before changing anything in this file.
*/
#include <types.h>
#include <lib.h>
#include <clock.h>
#include <thread.h>
#include <synch.h>
#include <test.h>
#include <kern/test161.h>
#include <spinlock.h>
/*
* Use these stubs to test your reader-writer locks.
*/
int rwtest(int nargs, char **args) {
(void)nargs;
(void)args;
kprintf_n("rwt1 unimplemented\n");
success(TEST161_FAIL, SECRET, "rwt1");
return 0;
}
int rwtest2(int nargs, char **args) {
(void)nargs;
(void)args;
kprintf_n("rwt2 unimplemented\n");
success(TEST161_FAIL, SECRET, "rwt2");
return 0;
}
int rwtest3(int nargs, char **args) {
(void)nargs;
(void)args;
kprintf_n("rwt3 unimplemented\n");
success(TEST161_FAIL, SECRET, "rwt3");
return 0;
}
int rwtest4(int nargs, char **args) {
(void)nargs;
(void)args;
kprintf_n("rwt4 unimplemented\n");
success(TEST161_FAIL, SECRET, "rwt4");
return 0;
}
int rwtest5(int nargs, char **args) {
(void)nargs;
(void)args;
kprintf_n("rwt5 unimplemented\n");
success(TEST161_FAIL, SECRET, "rwt5");
return 0;
}

688
kern/test/synchprobs.c Normal file
View File

@ -0,0 +1,688 @@
/*
* All the contents of this file are overwritten during automated
* testing. Please consider this before changing anything in this file.
*/
#include <types.h>
#include <lib.h>
#include <thread.h>
#include <test.h>
#include <current.h>
#include <synch.h>
#include <kern/test161.h>
#include <spinlock.h>
#define PROBLEMS_MAX_YIELDER 16
#define PROBLEMS_MAX_SPINNER 8192
#define TEST161_SUCCESS 0
#define TEST161_FAIL 1
/*
* Shared initialization routines
*/
static uint32_t startcount;
static struct lock *testlock;
static struct cv *startcv;
static struct semaphore *startsem;
static struct semaphore *endsem;
struct spinlock status_lock;
static bool test_status = TEST161_FAIL;
const char *test_message;
static
bool
failif(bool condition, const char *message) {
if (condition) {
spinlock_acquire(&status_lock);
test_status = TEST161_FAIL;
test_message = message;
spinlock_release(&status_lock);
}
return condition;
}
/*
* Helper function to initialize the thread pool.
*/
static
void
initialize_thread(volatile void* threads[], uint32_t index) {
failif((threads[index] != NULL), "failed: incorrect thread type");
threads[index] = curthread->t_stack;
}
/*
* Helper function to check whether current thread is valid.
*/
static
void
check_thread(volatile void* threads[], uint32_t index) {
failif((threads[index] != curthread->t_stack), "failed: incorrect thread type");
}
/*
* Driver code for the whalemating problem.
*/
#define NMATING 10
#define MALE 0
#define FEMALE 1
#define MATCHMAKER 2
#define CHECK_TIMES 32
static volatile int male_start_count;
static volatile int male_end_count;
static volatile int female_start_count;
static volatile int female_end_count;
static volatile int matchmaker_start_count;
static volatile int matchmaker_end_count;
static volatile int match_count;
static volatile int concurrent_matchmakers;
static volatile int max_concurrent_matchmakers;
static volatile void* whale_threads[3 * NMATING];
static volatile int whale_roles[3 * NMATING];
static struct semaphore *matcher_sem;
/*
* Enforce male_start() and male_end() called from male thread.
* Similar for female and matchmaker threads
*/
static
void
check_role(uint32_t index, int role) {
failif((whale_roles[index] != role), "failed: incorrect role");
}
static
void
male_wrapper(void * unused1, unsigned long index) {
(void)unused1;
random_yielder(4);
lock_acquire(testlock);
initialize_thread(whale_threads, (uint32_t)index);
whale_roles[index] = MALE;
lock_release(testlock);
male((uint32_t)index);
return;
}
void
male_start(uint32_t index) {
(void)index;
lock_acquire(testlock);
check_thread(whale_threads, index);
check_role(index, MALE);
male_start_count++;
kprintf_n("%s starting\n", curthread->t_name);
kprintf_t(".");
lock_release(testlock);
random_yielder(PROBLEMS_MAX_YIELDER);
random_spinner(PROBLEMS_MAX_SPINNER);
V(startsem);
}
void
male_end(uint32_t index) {
(void)index;
lock_acquire(testlock);
check_thread(whale_threads, index);
check_role(index, MALE);
male_end_count++;
kprintf_n("%s ending\n", curthread->t_name);
kprintf_t(".");
lock_release(testlock);
random_yielder(PROBLEMS_MAX_YIELDER);
random_spinner(PROBLEMS_MAX_SPINNER);
V(endsem);
}
static
void
female_wrapper(void * unused1, unsigned long index) {
(void)unused1;
random_yielder(4);
lock_acquire(testlock);
initialize_thread(whale_threads, (uint32_t)index);
whale_roles[index] = FEMALE;
lock_release(testlock);
female((uint32_t)index);
return;
}
void
female_start(uint32_t index) {
(void) index;
lock_acquire(testlock);
check_thread(whale_threads, index);
check_role(index, FEMALE);
female_start_count++;
kprintf_n("%s starting\n", curthread->t_name);
kprintf_t(".");
lock_release(testlock);
random_yielder(PROBLEMS_MAX_YIELDER);
random_spinner(PROBLEMS_MAX_SPINNER);
V(startsem);
}
void
female_end(uint32_t index) {
(void) index;
lock_acquire(testlock);
check_thread(whale_threads, index);
check_role(index, FEMALE);
female_end_count++;
kprintf_n("%s ending\n", curthread->t_name);
kprintf_t(".");
lock_release(testlock);
random_yielder(PROBLEMS_MAX_YIELDER);
random_spinner(PROBLEMS_MAX_SPINNER);
V(endsem);
}
static
void
matchmaker_wrapper(void * unused1, unsigned long index) {
(void)unused1;
random_yielder(4);
lock_acquire(testlock);
initialize_thread(whale_threads, (uint32_t)index);
whale_roles[index] = MATCHMAKER;
lock_release(testlock);
matchmaker((uint32_t)index);
return;
}
void
matchmaker_start(uint32_t index) {
(void)index;
P(matcher_sem);
lock_acquire(testlock);
check_thread(whale_threads, index);
check_role(index, MATCHMAKER);
matchmaker_start_count++;
concurrent_matchmakers++;
if (concurrent_matchmakers > max_concurrent_matchmakers) {
max_concurrent_matchmakers = concurrent_matchmakers;
}
kprintf_n("%s starting\n", curthread->t_name);
kprintf_t(".");
lock_release(testlock);
random_yielder(PROBLEMS_MAX_YIELDER);
random_spinner(PROBLEMS_MAX_SPINNER);
V(startsem);
}
void
matchmaker_end(uint32_t index) {
(void)index;
lock_acquire(testlock);
check_thread(whale_threads, index);
check_role(index, MATCHMAKER);
match_count++;
matchmaker_end_count++;
concurrent_matchmakers--;
kprintf_n("%s ending\n", curthread->t_name);
kprintf_t(".");
lock_release(testlock);
random_yielder(PROBLEMS_MAX_YIELDER);
random_spinner(PROBLEMS_MAX_SPINNER);
V(endsem);
}
int
whalemating(int nargs, char **args) {
(void) nargs;
(void) args;
int i, j, err = 0;
char name[32];
bool loop_status;
int total_count = 0;
male_start_count = 0 ;
male_end_count = 0 ;
female_start_count = 0 ;
female_end_count = 0 ;
matchmaker_start_count = 0;
matchmaker_end_count = 0;
match_count = 0;
concurrent_matchmakers = 0;
max_concurrent_matchmakers = 0;
kprintf_n("Starting sp1...\n");
kprintf_n("If this tests hangs, your solution is incorrect.\n");
testlock = lock_create("testlock");
if (testlock == NULL) {
panic("sp1: lock_create failed\n");
}
startsem = sem_create("startsem", 0);
if (startsem == NULL) {
panic("sp1: sem_create failed\n");
}
endsem = sem_create("endsem", 0);
if (endsem == NULL) {
panic("sp1: sem_create failed\n");
}
matcher_sem = sem_create("matcher_sem", 0);
if (matcher_sem == NULL) {
panic("sp1: sem_create failed\n");
}
spinlock_init(&status_lock);
test_status = TEST161_SUCCESS;
test_message = "";
whalemating_init();
/* Start males and females only. */
for (i = 0; i < 2; i++) {
for (j = 0; j < NMATING; j++) {
kprintf_t(".");
int index = (i * NMATING) + j;
whale_threads[index] = NULL;
switch (i) {
case 0:
snprintf(name, sizeof(name), "Male Whale Thread %d", index);
err = thread_fork(name, NULL, male_wrapper, NULL, index);
break;
case 1:
snprintf(name, sizeof(name), "Female Whale Thread %d", index);
err = thread_fork(name, NULL, female_wrapper, NULL, index);
break;
}
total_count += 1;
if (err) {
panic("sp1: thread_fork failed: (%s)\n", strerror(err));
}
}
}
/* Wait for males and females to start. */
for (i = 0; i < NMATING * 2; i++) {
kprintf_t(".");
P(startsem);
}
/* Make sure nothing is happening... */
loop_status = TEST161_SUCCESS;
for (i = 0; i < CHECK_TIMES && loop_status == TEST161_SUCCESS; i++) {
kprintf_t(".");
random_spinner(PROBLEMS_MAX_SPINNER);
lock_acquire(testlock);
if ((male_start_count != NMATING) || (female_start_count != NMATING) ||
(matchmaker_start_count + male_end_count + female_end_count + matchmaker_end_count != 0)) {
loop_status = TEST161_FAIL;
}
lock_release(testlock);
}
if (failif((loop_status == TEST161_FAIL), "failed: uncoordinated matchmaking is occurring")) {
goto done;
}
/* Create the matchmakers */
for (j = 0; j < NMATING; j++) {
kprintf_t(".");
int index = (2 * NMATING) + j;
whale_threads[index] = NULL;
snprintf(name, sizeof(name), "Matchmaker Whale Thread %d", index);
err = thread_fork(name, NULL, matchmaker_wrapper, NULL, index);
if (err) {
panic("sp1: thread_fork failed: (%s)\n", strerror(err));
}
total_count++;
}
/*
* Release a random number of matchmakers and wait for them and their
* matches to finish.
*/
int pivot = (random() % (NMATING - 2)) + 1;
for (i = 0; i < pivot; i++) {
kprintf_t(".");
V(matcher_sem);
}
for (i = 0; i < 3 * pivot; i++) {
kprintf_t(".");
P(endsem);
total_count--;
}
/* Make sure nothing else is happening... */
loop_status = TEST161_SUCCESS;
for (i = 0; i < CHECK_TIMES && loop_status == TEST161_SUCCESS; i++) {
kprintf_t(".");
random_spinner(PROBLEMS_MAX_SPINNER);
lock_acquire(testlock);
if ((male_start_count != NMATING) || (female_start_count != NMATING) ||
(matchmaker_start_count != pivot) || (male_end_count != pivot) ||
(female_end_count != pivot) || (matchmaker_end_count != pivot)) {
loop_status = TEST161_FAIL;
}
lock_release(testlock);
}
if (failif((loop_status == TEST161_FAIL), "failed: uncoordinated matchmaking is occurring")) {
goto done;
}
/*
* Release the rest of the matchmakers and wait for everyone to finish.
*/
for (i = pivot; i < NMATING; i++) {
kprintf_t(".");
V(matcher_sem);
}
for (i = 0; i < 3; i++) {
for (j = pivot; j < NMATING; j++) {
kprintf_t(".");
P(endsem);
total_count--;
}
}
failif((max_concurrent_matchmakers == 1), "failed: no matchmaker concurrency");
whalemating_cleanup();
done:
for (i = 0; i < total_count; i++) {
P(endsem);
}
lock_destroy(testlock);
sem_destroy(startsem);
sem_destroy(endsem);
sem_destroy(matcher_sem);
kprintf_t("\n");
if (test_status != TEST161_SUCCESS) {
secprintf(SECRET, test_message, "sp1");
}
success(test_status, SECRET, "sp1");
return 0;
}
/*
* Driver code for the stoplight problem.
*/
#define NCARS 64
#define NUM_QUADRANTS 4
#define UNKNOWN_CAR -1
#define PASSED_CAR -2
#define GO_STRAIGHT 0
#define TURN_LEFT 1
#define TURN_RIGHT 2
static volatile int quadrant_array[NUM_QUADRANTS];
static volatile int max_car_count;
static volatile int all_quadrant;
static volatile int car_locations[NCARS];
static volatile int car_directions[NCARS];
static volatile int car_turns[NCARS];
static volatile int car_turn_times[NCARS];
static volatile void* car_threads[NCARS];
static
void
initialize_car_thread(uint32_t index, uint32_t direction, uint32_t turn) {
initialize_thread(car_threads, index);
car_directions[index] = direction;
car_turns[index] = turn;
car_turn_times[index] = 0;
}
static
void
check_intersection() {
int n = 0;
for (int i = 0; i < NUM_QUADRANTS; i++) {
failif((quadrant_array[i] > 1), "failed: collision");
n += quadrant_array[i];
}
max_car_count = n > max_car_count ? n : max_car_count;
}
/*
* When car move, must call this function and hold a lock.
* It first checks current intersection status make sure no more than one car in one quadrant.
* Then it removes current car from previous location.
* In the end, it returns current car's index for inQuadrant, to let inQuadrant update car_locations array.
*/
static
int
move(uint32_t index) {
check_thread(car_threads, index);
check_intersection();
int pre_location = car_locations[index];
if (pre_location != UNKNOWN_CAR && pre_location != PASSED_CAR) {
quadrant_array[pre_location]--;
}
return pre_location;
}
static
void
turnright_wrapper(void *index, unsigned long direction)
{
random_yielder(4);
lock_acquire(testlock);
initialize_car_thread((uint32_t)index, (uint32_t)direction, TURN_RIGHT);
startcount--;
if (startcount == 0) {
cv_broadcast(startcv, testlock);
} else {
cv_wait(startcv, testlock);
}
lock_release(testlock);
turnright((uint32_t)direction, (uint32_t)index);
V(endsem);
return;
}
static
void
gostraight_wrapper(void *index, unsigned long direction)
{
random_yielder(4);
lock_acquire(testlock);
initialize_car_thread((uint32_t)index, (uint32_t)direction, GO_STRAIGHT);
startcount--;
if (startcount == 0) {
cv_broadcast(startcv, testlock);
} else {
cv_wait(startcv, testlock);
}
lock_release(testlock);
gostraight((uint32_t)direction, (uint32_t)index);
V(endsem);
return;
}
static
void
turnleft_wrapper(void *index, unsigned long direction)
{
random_yielder(4);
lock_acquire(testlock);
initialize_car_thread((uint32_t)index, (uint32_t)direction, TURN_LEFT);
startcount--;
if (startcount == 0) {
cv_broadcast(startcv, testlock);
} else {
cv_wait(startcv, testlock);
}
lock_release(testlock);
turnleft((uint32_t)direction, (uint32_t)index);
V(endsem);
return;
}
void
inQuadrant(int quadrant, uint32_t index) {
random_yielder(PROBLEMS_MAX_YIELDER);
random_spinner(PROBLEMS_MAX_SPINNER);
lock_acquire(testlock);
int pre_quadrant = move(index);
int target_quadrant = car_directions[index];
switch (car_turn_times[index]) {
case 0:
failif((pre_quadrant != UNKNOWN_CAR), "failed: invalid turn");
break;
case 1:
failif((pre_quadrant != target_quadrant), "failed: invalid turn");
target_quadrant = (target_quadrant + NUM_QUADRANTS - 1) % NUM_QUADRANTS;
break;
case 2:
target_quadrant = (target_quadrant + NUM_QUADRANTS - 1) % NUM_QUADRANTS;
failif((pre_quadrant != target_quadrant), "failed: invalid turn");
target_quadrant = (target_quadrant + NUM_QUADRANTS - 1) % NUM_QUADRANTS;
break;
default:
failif(true, "failed: invalid turn");
break;
}
failif((quadrant != target_quadrant), "failed: invalid turn");
car_turn_times[index]++;
failif((quadrant_array[quadrant] > 0), "failed: collision");
quadrant_array[quadrant]++;
car_locations[index] = quadrant;
all_quadrant++;
lock_release(testlock);
kprintf_n("%s in quadrant %d\n", curthread->t_name, quadrant);
}
void
leaveIntersection(uint32_t index) {
random_yielder(PROBLEMS_MAX_YIELDER);
random_spinner(PROBLEMS_MAX_SPINNER);
lock_acquire(testlock);
move(index);
switch (car_turns[index]) {
case GO_STRAIGHT:
failif((car_turn_times[index] != 2), "failed: incorrect turn");
break;
case TURN_LEFT:
failif((car_turn_times[index] != 3), "failed: incorrect turn");
break;
case TURN_RIGHT:
failif((car_turn_times[index] != 1), "failed: incorrect turn");
break;
default:
failif(true, "failed: incorrect turn");
break;
}
car_locations[index] = PASSED_CAR;
lock_release(testlock);
kprintf_n("%s left the intersection\n", curthread->t_name);
}
int stoplight(int nargs, char **args) {
(void) nargs;
(void) args;
int i, direction, turn, err = 0;
char name[32];
int required_quadrant = 0;
int passed = 0;
max_car_count = 0;
all_quadrant = 0;
kprintf_n("Starting sp2...\n");
kprintf_n("If this tests hangs, your solution is incorrect.\n");
for (i = 0; i < NUM_QUADRANTS; i++) {
quadrant_array[i] = 0;
}
for (i = 0; i < NCARS; i++) {
car_locations[i] = UNKNOWN_CAR;
car_threads[i] = NULL;
car_directions[i] = -1;
}
startcount = NCARS;
testlock = lock_create("testlock");
if (testlock == NULL) {
panic("sp2: lock_create failed\n");
}
startcv = cv_create("startcv");
if (startcv == NULL) {
panic("sp2: cv_create failed\n");
}
endsem = sem_create("endsem", 0);
if (endsem == NULL) {
panic("sp2: sem_create failed\n");
}
spinlock_init(&status_lock);
test_status = TEST161_SUCCESS;
stoplight_init();
for (i = 0; i < NCARS; i++) {
kprintf_t(".");
direction = random() % 4;
turn = random() % 3;
snprintf(name, sizeof(name), "Car Thread %d", i);
switch (turn) {
case GO_STRAIGHT:
err = thread_fork(name, NULL, gostraight_wrapper, (void *)i, direction);
required_quadrant += 2;
break;
case TURN_LEFT:
err = thread_fork(name, NULL, turnleft_wrapper, (void *)i, direction);
required_quadrant += 3;
break;
case TURN_RIGHT:
err = thread_fork(name, NULL, turnright_wrapper, (void *)i, direction);
required_quadrant += 1;
break;
}
if (err) {
panic("sp2: thread_fork failed: (%s)\n", strerror(err));
}
}
for (i = 0; i < NCARS; i++) {
kprintf_t(".");
P(endsem);
}
stoplight_cleanup();
for (i = 0; i < NCARS; i++) {
passed += car_locations[i] == PASSED_CAR ? 1 : 0;
}
if ((test_status == TEST161_SUCCESS) &&
(!(failif((passed != NCARS), "failed: not enough cars"))) &&
(!(failif((all_quadrant != required_quadrant), "failed: didn't do the right turns"))) &&
(!(failif((max_car_count <= 1), "failed: no concurrency achieved")))) {};
lock_destroy(testlock);
cv_destroy(startcv);
sem_destroy(endsem);
kprintf_t("\n");
if (test_status != TEST161_SUCCESS) {
secprintf(SECRET, test_message, "sp2");
}
success(test_status, SECRET, "sp2");
return 0;
}

View File

@ -29,6 +29,9 @@
/* /*
* Synchronization test code. * Synchronization test code.
*
* All the contents of this file are overwritten during automated
* testing. Please consider this before changing anything in this file.
*/ */
#include <types.h> #include <types.h>
@ -37,186 +40,423 @@
#include <thread.h> #include <thread.h>
#include <synch.h> #include <synch.h>
#include <test.h> #include <test.h>
#include <kern/test161.h>
#include <spinlock.h>
#define CREATELOOPS 8
#define NSEMLOOPS 63 #define NSEMLOOPS 63
#define NLOCKLOOPS 120 #define NLOCKLOOPS 120
#define NCVLOOPS 5 #define NCVLOOPS 5
#define NTHREADS 32 #define NTHREADS 32
#define SYNCHTEST_YIELDER_MAX 16
static volatile unsigned long testval1; static volatile unsigned long testval1;
static volatile unsigned long testval2; static volatile unsigned long testval2;
static volatile unsigned long testval3; static volatile unsigned long testval3;
static struct semaphore *testsem; static volatile int32_t testval4;
static struct lock *testlock;
static struct cv *testcv; static struct semaphore *testsem = NULL;
static struct semaphore *donesem; static struct semaphore *testsem2 = NULL;
static struct lock *testlock = NULL;
static struct lock *testlock2 = NULL;
static struct cv *testcv = NULL;
static struct semaphore *donesem = NULL;
struct spinlock status_lock;
static bool test_status = TEST161_FAIL;
static unsigned long semtest_current;
static static
void bool
inititems(void) failif(bool condition) {
{ if (condition) {
if (testsem==NULL) { spinlock_acquire(&status_lock);
testsem = sem_create("testsem", 2); test_status = TEST161_FAIL;
if (testsem == NULL) { spinlock_release(&status_lock);
panic("synchtest: sem_create failed\n");
}
}
if (testlock==NULL) {
testlock = lock_create("testlock");
if (testlock == NULL) {
panic("synchtest: lock_create failed\n");
}
}
if (testcv==NULL) {
testcv = cv_create("testlock");
if (testcv == NULL) {
panic("synchtest: cv_create failed\n");
}
}
if (donesem==NULL) {
donesem = sem_create("donesem", 0);
if (donesem == NULL) {
panic("synchtest: sem_create failed\n");
}
} }
return condition;
} }
static static
void void
semtestthread(void *junk, unsigned long num) semtestthread(void *junk, unsigned long num)
{ {
int i;
(void)junk; (void)junk;
int i;
random_yielder(4);
/* /*
* Only one of these should print at a time. * Only one of these should print at a time.
*/ */
P(testsem); P(testsem);
kprintf("Thread %2lu: ", num); semtest_current = num;
kprintf_n("Thread %2lu: ", num);
for (i=0; i<NSEMLOOPS; i++) { for (i=0; i<NSEMLOOPS; i++) {
kprintf("%c", (int)num+64); kprintf_t(".");
kprintf_n("%2lu", num);
random_yielder(4);
failif((semtest_current != num));
} }
kprintf("\n"); kprintf_n("\n");
V(donesem); V(donesem);
} }
int int
semtest(int nargs, char **args) semtest(int nargs, char **args)
{ {
int i, result;
(void)nargs; (void)nargs;
(void)args; (void)args;
inititems(); int i, result;
kprintf("Starting semaphore test...\n");
kprintf("If this hangs, it's broken: "); kprintf_n("Starting sem1...\n");
for (i=0; i<CREATELOOPS; i++) {
kprintf_t(".");
testsem = sem_create("testsem", 2);
if (testsem == NULL) {
panic("sem1: sem_create failed\n");
}
donesem = sem_create("donesem", 0);
if (donesem == NULL) {
panic("sem1: sem_create failed\n");
}
if (i != CREATELOOPS - 1) {
sem_destroy(testsem);
sem_destroy(donesem);
}
}
spinlock_init(&status_lock);
test_status = TEST161_SUCCESS;
kprintf_n("If this hangs, it's broken: ");
P(testsem); P(testsem);
P(testsem); P(testsem);
kprintf("ok\n"); kprintf_n("OK\n");
kprintf_t(".");
for (i=0; i<NTHREADS; i++) { for (i=0; i<NTHREADS; i++) {
kprintf_t(".");
result = thread_fork("semtest", NULL, semtestthread, NULL, i); result = thread_fork("semtest", NULL, semtestthread, NULL, i);
if (result) { if (result) {
panic("semtest: thread_fork failed: %s\n", panic("sem1: thread_fork failed: %s\n",
strerror(result)); strerror(result));
} }
} }
for (i=0; i<NTHREADS; i++) { for (i=0; i<NTHREADS; i++) {
kprintf_t(".");
V(testsem); V(testsem);
P(donesem); P(donesem);
} }
/* so we can run it again */ sem_destroy(testsem);
V(testsem); sem_destroy(donesem);
V(testsem); testsem = donesem = NULL;
kprintf_t("\n");
success(test_status, SECRET, "sem1");
kprintf("Semaphore test done.\n");
return 0; return 0;
} }
static
void
fail(unsigned long num, const char *msg)
{
kprintf("thread %lu: Mismatch on %s\n", num, msg);
kprintf("Test failed\n");
lock_release(testlock);
V(donesem);
thread_exit();
}
static static
void void
locktestthread(void *junk, unsigned long num) locktestthread(void *junk, unsigned long num)
{ {
int i;
(void)junk; (void)junk;
int i;
for (i=0; i<NLOCKLOOPS; i++) { for (i=0; i<NLOCKLOOPS; i++) {
kprintf_t(".");
KASSERT(!(lock_do_i_hold(testlock)));
lock_acquire(testlock); lock_acquire(testlock);
KASSERT(lock_do_i_hold(testlock));
random_yielder(4);
testval1 = num; testval1 = num;
testval2 = num*num; testval2 = num*num;
testval3 = num%3; testval3 = num%3;
if (testval2 != testval1*testval1) { if (testval2 != testval1*testval1) {
fail(num, "testval2/testval1"); goto fail;
} }
random_yielder(4);
KASSERT(lock_do_i_hold(testlock));
if (testval2%3 != (testval3*testval3)%3) { if (testval2%3 != (testval3*testval3)%3) {
fail(num, "testval2/testval3"); goto fail;
} }
random_yielder(4);
KASSERT(lock_do_i_hold(testlock));
if (testval3 != testval1%3) { if (testval3 != testval1%3) {
fail(num, "testval3/testval1"); goto fail;
} }
random_yielder(4);
KASSERT(lock_do_i_hold(testlock));
if (testval1 != num) { if (testval1 != num) {
fail(num, "testval1/num"); goto fail;
} }
random_yielder(4);
KASSERT(lock_do_i_hold(testlock));
if (testval2 != num*num) { if (testval2 != num*num) {
fail(num, "testval2/num"); goto fail;
} }
random_yielder(4);
KASSERT(lock_do_i_hold(testlock));
if (testval3 != num%3) { if (testval3 != num%3) {
fail(num, "testval3/num"); goto fail;
} }
random_yielder(4);
KASSERT(lock_do_i_hold(testlock));
if (!(lock_do_i_hold(testlock))) {
goto fail;
}
random_yielder(4);
KASSERT(lock_do_i_hold(testlock));
lock_release(testlock); lock_release(testlock);
KASSERT(!(lock_do_i_hold(testlock)));
} }
V(donesem);
}
/* Check for solutions that don't track ownership properly */
for (i=0; i<NLOCKLOOPS; i++) {
kprintf_t(".");
if (lock_do_i_hold(testlock)) {
goto fail2;
}
}
V(donesem);
return;
fail:
lock_release(testlock);
fail2:
failif(true);
V(donesem);
return;
}
int int
locktest(int nargs, char **args) locktest(int nargs, char **args)
{ {
int i, result;
(void)nargs; (void)nargs;
(void)args; (void)args;
inititems(); int i, result;
kprintf("Starting lock test...\n");
kprintf_n("Starting lt1...\n");
for (i=0; i<CREATELOOPS; i++) {
kprintf_t(".");
testlock = lock_create("testlock");
if (testlock == NULL) {
panic("lt1: lock_create failed\n");
}
donesem = sem_create("donesem", 0);
if (donesem == NULL) {
panic("lt1: sem_create failed\n");
}
if (i != CREATELOOPS - 1) {
lock_destroy(testlock);
sem_destroy(donesem);
}
}
spinlock_init(&status_lock);
test_status = TEST161_SUCCESS;
for (i=0; i<NTHREADS; i++) { for (i=0; i<NTHREADS; i++) {
result = thread_fork("synchtest", NULL, locktestthread, kprintf_t(".");
NULL, i); result = thread_fork("synchtest", NULL, locktestthread, NULL, i);
if (result) { if (result) {
panic("locktest: thread_fork failed: %s\n", panic("lt1: thread_fork failed: %s\n", strerror(result));
strerror(result));
} }
} }
for (i=0; i<NTHREADS; i++) { for (i=0; i<NTHREADS; i++) {
kprintf_t(".");
P(donesem); P(donesem);
} }
kprintf("Lock test done.\n"); lock_destroy(testlock);
sem_destroy(donesem);
testlock = NULL;
donesem = NULL;
kprintf_t("\n");
success(test_status, SECRET, "lt1");
return 0;
}
/*
* Note that the following tests that panic on success do minimal cleanup
* afterward. This is to avoid causing a panic that could be unintentiontally
* considered a success signal by test161. As a result, they leak memory,
* don't destroy synchronization primitives, etc.
*/
int
locktest2(int nargs, char **args) {
(void)nargs;
(void)args;
kprintf_n("Starting lt2...\n");
kprintf_n("(This test panics on success!)\n");
testlock = lock_create("testlock");
if (testlock == NULL) {
panic("lt2: lock_create failed\n");
}
secprintf(SECRET, "Should panic...", "lt2");
lock_release(testlock);
/* Should not get here on success. */
success(TEST161_FAIL, SECRET, "lt2");
/* Don't do anything that could panic. */
testlock = NULL;
return 0;
}
int
locktest3(int nargs, char **args) {
(void)nargs;
(void)args;
kprintf_n("Starting lt3...\n");
kprintf_n("(This test panics on success!)\n");
testlock = lock_create("testlock");
if (testlock == NULL) {
panic("lt3: lock_create failed\n");
}
secprintf(SECRET, "Should panic...", "lt3");
lock_acquire(testlock);
lock_destroy(testlock);
/* Should not get here on success. */
success(TEST161_FAIL, SECRET, "lt3");
/* Don't do anything that could panic. */
testlock = NULL;
return 0;
}
/*
* Used by both lt4 and lt5 below. Simply acquires a lock in a separate
* thread. Uses a semaphore as a barrier to make sure it gets the lock before
* the driver completes.
*/
static
void
locktestacquirer(void * junk, unsigned long num)
{
(void)junk;
(void)num;
lock_acquire(testlock);
V(donesem);
return;
}
int
locktest4(int nargs, char **args) {
(void) nargs;
(void) args;
int result;
kprintf_n("Starting lt4...\n");
kprintf_n("(This test panics on success!)\n");
testlock = lock_create("testlock");
if (testlock == NULL) {
panic("lt4: lock_create failed\n");
}
donesem = sem_create("donesem", 0);
if (donesem == NULL) {
lock_destroy(testlock);
panic("lt4: sem_create failed\n");
}
result = thread_fork("lt4", NULL, locktestacquirer, NULL, 0);
if (result) {
panic("lt4: thread_fork failed: %s\n", strerror(result));
}
P(donesem);
secprintf(SECRET, "Should panic...", "lt4");
lock_release(testlock);
/* Should not get here on success. */
success(TEST161_FAIL, SECRET, "lt4");
/* Don't do anything that could panic. */
testlock = NULL;
donesem = NULL;
return 0;
}
int
locktest5(int nargs, char **args) {
(void) nargs;
(void) args;
int result;
kprintf_n("Starting lt5...\n");
kprintf_n("(This test panics on success!)\n");
testlock = lock_create("testlock");
if (testlock == NULL) {
panic("lt5: lock_create failed\n");
}
donesem = sem_create("donesem", 0);
if (donesem == NULL) {
lock_destroy(testlock);
panic("lt5: sem_create failed\n");
}
result = thread_fork("lt5", NULL, locktestacquirer, NULL, 0);
if (result) {
panic("lt5: thread_fork failed: %s\n", strerror(result));
}
P(donesem);
secprintf(SECRET, "Should panic...", "lt5");
KASSERT(lock_do_i_hold(testlock));
/* Should not get here on success. */
success(TEST161_FAIL, SECRET, "lt5");
/* Don't do anything that could panic. */
testlock = NULL;
donesem = NULL;
return 0; return 0;
} }
@ -224,35 +464,38 @@ static
void void
cvtestthread(void *junk, unsigned long num) cvtestthread(void *junk, unsigned long num)
{ {
(void)junk;
int i; int i;
volatile int j; volatile int j;
struct timespec ts1, ts2; struct timespec ts1, ts2;
(void)junk;
for (i=0; i<NCVLOOPS; i++) { for (i=0; i<NCVLOOPS; i++) {
kprintf_t(".");
lock_acquire(testlock); lock_acquire(testlock);
while (testval1 != num) { while (testval1 != num) {
testval2 = 0;
random_yielder(4);
gettime(&ts1); gettime(&ts1);
cv_wait(testcv, testlock); cv_wait(testcv, testlock);
gettime(&ts2); gettime(&ts2);
random_yielder(4);
/* ts2 -= ts1 */ /* ts2 -= ts1 */
timespec_sub(&ts2, &ts1, &ts2); timespec_sub(&ts2, &ts1, &ts2);
/* Require at least 2000 cpu cycles (we're 25mhz) */ /* Require at least 2000 cpu cycles (we're 25mhz) */
if (ts2.tv_sec == 0 && ts2.tv_nsec < 40*2000) { if (ts2.tv_sec == 0 && ts2.tv_nsec < 40*2000) {
kprintf("cv_wait took only %u ns\n", kprintf_n("cv_wait took only %u ns\n", ts2.tv_nsec);
ts2.tv_nsec); kprintf_n("That's too fast... you must be busy-looping\n");
kprintf("That's too fast... you must be " failif(true);
"busy-looping\n");
V(donesem); V(donesem);
thread_exit(); thread_exit();
} }
testval2 = 0xFFFFFFFF;
} }
kprintf("Thread %lu\n", num); testval2 = num;
testval1 = (testval1 + NTHREADS - 1)%NTHREADS;
/* /*
* loop a little while to make sure we can measure the * loop a little while to make sure we can measure the
@ -260,7 +503,13 @@ cvtestthread(void *junk, unsigned long num)
*/ */
for (j=0; j<3000; j++); for (j=0; j<3000; j++);
random_yielder(4);
cv_broadcast(testcv, testlock); cv_broadcast(testcv, testlock);
random_yielder(4);
failif((testval1 != testval2));
kprintf_n("Thread %lu\n", testval2);
testval1 = (testval1 + NTHREADS - 1) % NTHREADS;
lock_release(testlock); lock_release(testlock);
} }
V(donesem); V(donesem);
@ -269,30 +518,57 @@ cvtestthread(void *junk, unsigned long num)
int int
cvtest(int nargs, char **args) cvtest(int nargs, char **args)
{ {
int i, result;
(void)nargs; (void)nargs;
(void)args; (void)args;
inititems(); int i, result;
kprintf("Starting CV test...\n");
kprintf("Threads should print out in reverse order.\n"); kprintf_n("Starting cvt1...\n");
for (i=0; i<CREATELOOPS; i++) {
kprintf_t(".");
testlock = lock_create("testlock");
if (testlock == NULL) {
panic("cvt1: lock_create failed\n");
}
testcv = cv_create("testcv");
if (testcv == NULL) {
panic("cvt1: cv_create failed\n");
}
donesem = sem_create("donesem", 0);
if (donesem == NULL) {
panic("cvt1: sem_create failed\n");
}
if (i != CREATELOOPS - 1) {
lock_destroy(testlock);
cv_destroy(testcv);
sem_destroy(donesem);
}
}
spinlock_init(&status_lock);
test_status = TEST161_SUCCESS;
testval1 = NTHREADS-1; testval1 = NTHREADS-1;
for (i=0; i<NTHREADS; i++) { for (i=0; i<NTHREADS; i++) {
result = thread_fork("synchtest", NULL, cvtestthread, NULL, i); kprintf_t(".");
result = thread_fork("cvt1", NULL, cvtestthread, NULL, (long unsigned) i);
if (result) { if (result) {
panic("cvtest: thread_fork failed: %s\n", panic("cvt1: thread_fork failed: %s\n", strerror(result));
strerror(result));
} }
} }
for (i=0; i<NTHREADS; i++) { for (i=0; i<NTHREADS; i++) {
kprintf_t(".");
P(donesem); P(donesem);
} }
kprintf("CV test done\n"); lock_destroy(testlock);
cv_destroy(testcv);
sem_destroy(donesem);
testlock = NULL;
testcv = NULL;
donesem = NULL;
kprintf_t("\n");
success(test_status, SECRET, "cvt1");
return 0; return 0;
} }
@ -318,19 +594,28 @@ static
void void
sleepthread(void *junk1, unsigned long junk2) sleepthread(void *junk1, unsigned long junk2)
{ {
unsigned i, j;
(void)junk1; (void)junk1;
(void)junk2; (void)junk2;
unsigned i, j;
random_yielder(4);
for (j=0; j<NLOOPS; j++) { for (j=0; j<NLOOPS; j++) {
kprintf_t(".");
for (i=0; i<NCVS; i++) { for (i=0; i<NCVS; i++) {
lock_acquire(testlocks[i]); lock_acquire(testlocks[i]);
random_yielder(4);
V(gatesem); V(gatesem);
random_yielder(4);
spinlock_acquire(&status_lock);
testval4++;
spinlock_release(&status_lock);
cv_wait(testcvs[i], testlocks[i]); cv_wait(testcvs[i], testlocks[i]);
random_yielder(4);
lock_release(testlocks[i]); lock_release(testlocks[i]);
} }
kprintf("sleepthread: %u\n", j); kprintf_n("sleepthread: %u\n", j);
} }
V(exitsem); V(exitsem);
} }
@ -339,19 +624,28 @@ static
void void
wakethread(void *junk1, unsigned long junk2) wakethread(void *junk1, unsigned long junk2)
{ {
unsigned i, j;
(void)junk1; (void)junk1;
(void)junk2; (void)junk2;
unsigned i, j;
random_yielder(4);
for (j=0; j<NLOOPS; j++) { for (j=0; j<NLOOPS; j++) {
kprintf_t(".");
for (i=0; i<NCVS; i++) { for (i=0; i<NCVS; i++) {
random_yielder(4);
P(gatesem); P(gatesem);
random_yielder(4);
lock_acquire(testlocks[i]); lock_acquire(testlocks[i]);
random_yielder(4);
testval4--;
failif((testval4 != 0));
cv_signal(testcvs[i], testlocks[i]); cv_signal(testcvs[i], testlocks[i]);
random_yielder(4);
lock_release(testlocks[i]); lock_release(testlocks[i]);
} }
kprintf("wakethread: %u\n", j); kprintf_n("wakethread: %u\n", j);
} }
V(exitsem); V(exitsem);
} }
@ -359,30 +653,44 @@ wakethread(void *junk1, unsigned long junk2)
int int
cvtest2(int nargs, char **args) cvtest2(int nargs, char **args)
{ {
unsigned i;
int result;
(void)nargs; (void)nargs;
(void)args; (void)args;
unsigned i;
int result;
kprintf_n("Starting cvt2...\n");
for (i=0; i<CREATELOOPS; i++) {
kprintf_t(".");
gatesem = sem_create("gatesem", 0);
if (gatesem == NULL) {
panic("cvt2: sem_create failed\n");
}
exitsem = sem_create("exitsem", 0);
if (exitsem == NULL) {
panic("cvt2: sem_create failed\n");
}
if (i != CREATELOOPS - 1) {
sem_destroy(gatesem);
sem_destroy(exitsem);
}
}
for (i=0; i<NCVS; i++) { for (i=0; i<NCVS; i++) {
kprintf_t(".");
testlocks[i] = lock_create("cvtest2 lock"); testlocks[i] = lock_create("cvtest2 lock");
testcvs[i] = cv_create("cvtest2 cv"); testcvs[i] = cv_create("cvtest2 cv");
} }
gatesem = sem_create("gatesem", 0); spinlock_init(&status_lock);
exitsem = sem_create("exitsem", 0); test_status = TEST161_SUCCESS;
kprintf("cvtest2...\n"); result = thread_fork("cvt2", NULL, sleepthread, NULL, 0);
result = thread_fork("cvtest2", NULL, sleepthread, NULL, 0);
if (result) { if (result) {
panic("cvtest2: thread_fork failed\n"); panic("cvt2: thread_fork failed\n");
} }
result = thread_fork("cvtest2", NULL, wakethread, NULL, 0); result = thread_fork("cvt2", NULL, wakethread, NULL, 0);
if (result) { if (result) {
panic("cvtest2: thread_fork failed\n"); panic("cvt2: thread_fork failed\n");
} }
P(exitsem); P(exitsem);
P(exitsem); P(exitsem);
@ -390,12 +698,194 @@ cvtest2(int nargs, char **args)
sem_destroy(gatesem); sem_destroy(gatesem);
exitsem = gatesem = NULL; exitsem = gatesem = NULL;
for (i=0; i<NCVS; i++) { for (i=0; i<NCVS; i++) {
kprintf_t(".");
lock_destroy(testlocks[i]); lock_destroy(testlocks[i]);
cv_destroy(testcvs[i]); cv_destroy(testcvs[i]);
testlocks[i] = NULL; testlocks[i] = NULL;
testcvs[i] = NULL; testcvs[i] = NULL;
} }
kprintf("cvtest2 done\n"); kprintf_t("\n");
success(test_status, SECRET, "cvt2");
return 0;
}
int
cvtest3(int nargs, char **args) {
(void)nargs;
(void)args;
kprintf_n("Starting cvt3...\n");
kprintf_n("(This test panics on success!)\n");
testlock = lock_create("testlock");
if (testlock == NULL) {
panic("cvt3: lock_create failed\n");
}
testcv = cv_create("testcv");
if (testcv == NULL) {
panic("cvt3: cv_create failed\n");
}
secprintf(SECRET, "Should panic...", "cvt3");
cv_wait(testcv, testlock);
/* Should not get here on success. */
success(TEST161_FAIL, SECRET, "cvt3");
lock_destroy(testlock);
cv_destroy(testcv);
testcv = NULL;
testlock = NULL;
return 0;
}
int
cvtest4(int nargs, char **args) {
(void)nargs;
(void)args;
kprintf_n("Starting cvt4...\n");
kprintf_n("(This test panics on success!)\n");
testlock = lock_create("testlock");
if (testlock == NULL) {
panic("cvt4: lock_create failed\n");
}
testcv = cv_create("testcv");
if (testcv == NULL) {
panic("cvt4: cv_create failed\n");
}
secprintf(SECRET, "Should panic...", "cvt4");
cv_broadcast(testcv, testlock);
/* Should not get here on success. */
success(TEST161_FAIL, SECRET, "cvt4");
lock_destroy(testlock);
cv_destroy(testcv);
testcv = NULL;
testlock = NULL;
return 0;
}
static
void
sleeperthread(void *junk1, unsigned long junk2) {
(void)junk1;
(void)junk2;
random_yielder(4);
lock_acquire(testlock);
random_yielder(4);
failif((testval1 != 0));
testval1 = 1;
cv_signal(testcv, testlock);
random_yielder(4);
cv_wait(testcv, testlock);
failif((testval1 != 3));
testval1 = 4;
random_yielder(4);
lock_release(testlock);
random_yielder(4);
V(exitsem);
}
static
void
wakerthread(void *junk1, unsigned long junk2) {
(void)junk1;
(void)junk2;
random_yielder(4);
lock_acquire(testlock2);
failif((testval1 != 2));
testval1 = 3;
random_yielder(4);
cv_signal(testcv, testlock2);
random_yielder(4);
lock_release(testlock2);
random_yielder(4);
V(exitsem);
}
int
cvtest5(int nargs, char **args) {
(void)nargs;
(void)args;
int result;
kprintf_n("Starting cvt5...\n");
testlock = lock_create("testlock");
if (testlock == NULL) {
panic("cvt5: lock_create failed\n");
}
testlock2 = lock_create("testlock2");
if (testlock == NULL) {
panic("cvt5: lock_create failed\n");
}
testcv = cv_create("testcv");
if (testcv == NULL) {
panic("cvt5: cv_create failed\n");
}
exitsem = sem_create("exitsem", 0);
if (exitsem == NULL) {
panic("cvt5: sem_create failed\n");
}
spinlock_init(&status_lock);
test_status = TEST161_SUCCESS;
testval1 = 0;
lock_acquire(testlock);
lock_acquire(testlock2);
result = thread_fork("cvt5", NULL, sleeperthread, NULL, 0);
if (result) {
panic("cvt5: thread_fork failed\n");
}
result = thread_fork("cvt5", NULL, wakerthread, NULL, 0);
if (result) {
panic("cvt5: thread_fork failed\n");
}
random_yielder(4);
cv_wait(testcv, testlock);
failif((testval1 != 1));
testval1 = 2;
random_yielder(4);
lock_release(testlock);
random_yielder(4);
lock_release(testlock2);
P(exitsem);
P(exitsem);
failif((testval1 != 4));
sem_destroy(exitsem);
cv_destroy(testcv);
lock_destroy(testlock2);
lock_destroy(testlock);
success(test_status, SECRET, "cvt5");
exitsem = NULL;
testcv = NULL;
testlock2 = NULL;
testlock = NULL;
testsem2 = NULL;
testsem = NULL;
return 0; return 0;
} }

View File

@ -66,10 +66,7 @@ fakethread_create(const char *name)
} }
/* ignore most of the fields, zero everything for tidiness */ /* ignore most of the fields, zero everything for tidiness */
bzero(t, sizeof(*t)); bzero(t, sizeof(*t));
t->t_name = kstrdup(name); strcpy(t->t_name, name);
if (t->t_name == NULL) {
panic("threadlisttest: Out of memory\n");
}
t->t_stack = FAKE_MAGIC; t->t_stack = FAKE_MAGIC;
threadlistnode_init(&t->t_listnode, t); threadlistnode_init(&t->t_listnode, t);
return t; return t;
@ -84,7 +81,6 @@ fakethread_destroy(struct thread *t)
{ {
KASSERT(t->t_stack == FAKE_MAGIC); KASSERT(t->t_stack == FAKE_MAGIC);
threadlistnode_cleanup(&t->t_listnode); threadlistnode_cleanup(&t->t_listnode);
kfree(t->t_name);
kfree(t); kfree(t);
} }

View File

@ -44,7 +44,9 @@
// //
// Semaphore. // Semaphore.
struct semaphore *sem_create(const char *name, unsigned initial_count) { struct semaphore *
sem_create(const char *name, unsigned initial_count)
{
struct semaphore *sem; struct semaphore *sem;
sem = kmalloc(sizeof(*sem)); sem = kmalloc(sizeof(*sem));
@ -71,7 +73,9 @@ struct semaphore *sem_create(const char *name, unsigned initial_count) {
return sem; return sem;
} }
void sem_destroy(struct semaphore *sem) { void
sem_destroy(struct semaphore *sem)
{
KASSERT(sem != NULL); KASSERT(sem != NULL);
/* wchan_cleanup will assert if anyone's waiting on it */ /* wchan_cleanup will assert if anyone's waiting on it */
@ -81,7 +85,9 @@ void sem_destroy(struct semaphore *sem) {
kfree(sem); kfree(sem);
} }
void P(struct semaphore *sem) { void
P(struct semaphore *sem)
{
KASSERT(sem != NULL); KASSERT(sem != NULL);
/* /*
@ -114,7 +120,9 @@ void P(struct semaphore *sem) {
spinlock_release(&sem->sem_lock); spinlock_release(&sem->sem_lock);
} }
void V(struct semaphore *sem) { void
V(struct semaphore *sem)
{
KASSERT(sem != NULL); KASSERT(sem != NULL);
spinlock_acquire(&sem->sem_lock); spinlock_acquire(&sem->sem_lock);
@ -130,7 +138,9 @@ void V(struct semaphore *sem) {
// //
// Lock. // Lock.
struct lock *lock_create(const char *name) { struct lock *
lock_create(const char *name)
{
struct lock *lock; struct lock *lock;
lock = kmalloc(sizeof(*lock)); lock = kmalloc(sizeof(*lock));
@ -151,7 +161,9 @@ struct lock *lock_create(const char *name) {
return lock; return lock;
} }
void lock_destroy(struct lock *lock) { void
lock_destroy(struct lock *lock)
{
KASSERT(lock != NULL); KASSERT(lock != NULL);
// add stuff here as needed // add stuff here as needed
@ -160,28 +172,34 @@ void lock_destroy(struct lock *lock) {
kfree(lock); kfree(lock);
} }
void lock_acquire(struct lock *lock) { void
lock_acquire(struct lock *lock)
{
/* Call this (atomically) before waiting for a lock */ /* Call this (atomically) before waiting for a lock */
// HANGMAN_WAIT(&curthread->t_hangman, &lock->lk_hangman); //HANGMAN_WAIT(&curthread->t_hangman, &lock->lk_hangman);
// Write this // Write this
(void)lock; // suppress warning until code gets written (void)lock; // suppress warning until code gets written
/* Call this (atomically) once the lock is acquired */ /* Call this (atomically) once the lock is acquired */
// HANGMAN_ACQUIRE(&curthread->t_hangman, &lock->lk_hangman); //HANGMAN_ACQUIRE(&curthread->t_hangman, &lock->lk_hangman);
} }
void lock_release(struct lock *lock) { void
lock_release(struct lock *lock)
{
/* Call this (atomically) when the lock is released */ /* Call this (atomically) when the lock is released */
// HANGMAN_RELEASE(&curthread->t_hangman, &lock->lk_hangman); //HANGMAN_RELEASE(&curthread->t_hangman, &lock->lk_hangman);
// Write this // Write this
(void)lock; // suppress warning until code gets written (void)lock; // suppress warning until code gets written
} }
bool lock_do_i_hold(struct lock *lock) { bool
lock_do_i_hold(struct lock *lock)
{
// Write this // Write this
(void)lock; // suppress warning until code gets written (void)lock; // suppress warning until code gets written
@ -193,7 +211,10 @@ bool lock_do_i_hold(struct lock *lock) {
// //
// CV // CV
struct cv *cv_create(const char *name) {
struct cv *
cv_create(const char *name)
{
struct cv *cv; struct cv *cv;
cv = kmalloc(sizeof(*cv)); cv = kmalloc(sizeof(*cv));
@ -202,7 +223,7 @@ struct cv *cv_create(const char *name) {
} }
cv->cv_name = kstrdup(name); cv->cv_name = kstrdup(name);
if (cv->cv_name == NULL) { if (cv->cv_name==NULL) {
kfree(cv); kfree(cv);
return NULL; return NULL;
} }
@ -212,7 +233,9 @@ struct cv *cv_create(const char *name) {
return cv; return cv;
} }
void cv_destroy(struct cv *cv) { void
cv_destroy(struct cv *cv)
{
KASSERT(cv != NULL); KASSERT(cv != NULL);
// add stuff here as needed // add stuff here as needed
@ -221,19 +244,25 @@ void cv_destroy(struct cv *cv) {
kfree(cv); kfree(cv);
} }
void cv_wait(struct cv *cv, struct lock *lock) { void
cv_wait(struct cv *cv, struct lock *lock)
{
// Write this // Write this
(void)cv; // suppress warning until code gets written (void)cv; // suppress warning until code gets written
(void)lock; // suppress warning until code gets written (void)lock; // suppress warning until code gets written
} }
void cv_signal(struct cv *cv, struct lock *lock) { void
cv_signal(struct cv *cv, struct lock *lock)
{
// Write this // Write this
(void)cv; // suppress warning until code gets written (void)cv; // suppress warning until code gets written
(void)lock; // suppress warning until code gets written (void)lock; // suppress warning until code gets written
} }
void cv_broadcast(struct cv *cv, struct lock *lock) { void
cv_broadcast(struct cv *cv, struct lock *lock)
{
// Write this // Write this
(void)cv; // suppress warning until code gets written (void)cv; // suppress warning until code gets written
(void)lock; // suppress warning until code gets written (void)lock; // suppress warning until code gets written

View File

@ -51,6 +51,7 @@
#include <mainbus.h> #include <mainbus.h>
#include <vnode.h> #include <vnode.h>
/* Magic number used as a guard value on kernel thread stacks. */ /* Magic number used as a guard value on kernel thread stacks. */
#define THREAD_STACK_MAGIC 0xbaadf00d #define THREAD_STACK_MAGIC 0xbaadf00d
@ -64,10 +65,16 @@ struct wchan {
DECLARRAY(cpu, static __UNUSED inline); DECLARRAY(cpu, static __UNUSED inline);
DEFARRAY(cpu, static __UNUSED inline); DEFARRAY(cpu, static __UNUSED inline);
static struct cpuarray allcpus; static struct cpuarray allcpus;
unsigned num_cpus;
/* Used to wait for secondary CPUs to come online. */ /* Used to wait for secondary CPUs to come online. */
static struct semaphore *cpu_startup_sem; static struct semaphore *cpu_startup_sem;
/* Used to synchronize exit cleanup. */
unsigned thread_count = 0;
static struct spinlock thread_count_lock = SPINLOCK_INITIALIZER;
static struct wchan *thread_count_wchan;
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
/* /*
@ -75,7 +82,10 @@ static struct semaphore *cpu_startup_sem;
* (sometimes) catch kernel stack overflows. Use thread_checkstack() * (sometimes) catch kernel stack overflows. Use thread_checkstack()
* to test this. * to test this.
*/ */
static void thread_checkstack_init(struct thread *thread) { static
void
thread_checkstack_init(struct thread *thread)
{
((uint32_t *)thread->t_stack)[0] = THREAD_STACK_MAGIC; ((uint32_t *)thread->t_stack)[0] = THREAD_STACK_MAGIC;
((uint32_t *)thread->t_stack)[1] = THREAD_STACK_MAGIC; ((uint32_t *)thread->t_stack)[1] = THREAD_STACK_MAGIC;
((uint32_t *)thread->t_stack)[2] = THREAD_STACK_MAGIC; ((uint32_t *)thread->t_stack)[2] = THREAD_STACK_MAGIC;
@ -92,12 +102,15 @@ static void thread_checkstack_init(struct thread *thread) {
* cannot be freed (which in turn is the case if the stack is the boot * cannot be freed (which in turn is the case if the stack is the boot
* stack, and the thread is the boot thread) this doesn't do anything. * stack, and the thread is the boot thread) this doesn't do anything.
*/ */
static void thread_checkstack(struct thread *thread) { static
void
thread_checkstack(struct thread *thread)
{
if (thread->t_stack != NULL) { if (thread->t_stack != NULL) {
KASSERT(((uint32_t *)thread->t_stack)[0] == THREAD_STACK_MAGIC); KASSERT(((uint32_t*)thread->t_stack)[0] == THREAD_STACK_MAGIC);
KASSERT(((uint32_t *)thread->t_stack)[1] == THREAD_STACK_MAGIC); KASSERT(((uint32_t*)thread->t_stack)[1] == THREAD_STACK_MAGIC);
KASSERT(((uint32_t *)thread->t_stack)[2] == THREAD_STACK_MAGIC); KASSERT(((uint32_t*)thread->t_stack)[2] == THREAD_STACK_MAGIC);
KASSERT(((uint32_t *)thread->t_stack)[3] == THREAD_STACK_MAGIC); KASSERT(((uint32_t*)thread->t_stack)[3] == THREAD_STACK_MAGIC);
} }
} }
@ -105,21 +118,23 @@ static void thread_checkstack(struct thread *thread) {
* Create a thread. This is used both to create a first thread * Create a thread. This is used both to create a first thread
* for each CPU and to create subsequent forked threads. * for each CPU and to create subsequent forked threads.
*/ */
static struct thread *thread_create(const char *name) { static
struct thread *
thread_create(const char *name)
{
struct thread *thread; struct thread *thread;
DEBUGASSERT(name != NULL); DEBUGASSERT(name != NULL);
if (strlen(name) > MAX_NAME_LENGTH) {
return NULL;
}
thread = kmalloc(sizeof(*thread)); thread = kmalloc(sizeof(*thread));
if (thread == NULL) { if (thread == NULL) {
return NULL; return NULL;
} }
thread->t_name = kstrdup(name); strcpy(thread->t_name, name);
if (thread->t_name == NULL) {
kfree(thread);
return NULL;
}
thread->t_wchan_name = "NEW"; thread->t_wchan_name = "NEW";
thread->t_state = S_READY; thread->t_state = S_READY;
@ -150,7 +165,9 @@ static struct thread *thread_create(const char *name) {
* board config or whatnot) is tracked separately because it is not * board config or whatnot) is tracked separately because it is not
* necessarily anything sane or meaningful. * necessarily anything sane or meaningful.
*/ */
struct cpu *cpu_create(unsigned hardware_number) { struct cpu *
cpu_create(unsigned hardware_number)
{
struct cpu *c; struct cpu *c;
int result; int result;
char namebuf[16]; char namebuf[16];
@ -196,7 +213,8 @@ struct cpu *cpu_create(unsigned hardware_number) {
* make it possible to free the boot stack?) * make it possible to free the boot stack?)
*/ */
/*c->c_curthread->t_stack = ... */ /*c->c_curthread->t_stack = ... */
} else { }
else {
c->c_curthread->t_stack = kmalloc(STACK_SIZE); c->c_curthread->t_stack = kmalloc(STACK_SIZE);
if (c->c_curthread->t_stack == NULL) { if (c->c_curthread->t_stack == NULL) {
panic("cpu_create: couldn't allocate stack"); panic("cpu_create: couldn't allocate stack");
@ -246,16 +264,17 @@ struct cpu *cpu_create(unsigned hardware_number) {
* Nor can it be called on a running thread. * Nor can it be called on a running thread.
* *
* (Freeing the stack you're actually using to run is ... inadvisable.) * (Freeing the stack you're actually using to run is ... inadvisable.)
*
* Thread destroy should finish the process of cleaning up a thread started by
* thread_exit.
*/ */
static void thread_destroy(struct thread *thread) { static
void
thread_destroy(struct thread *thread)
{
KASSERT(thread != curthread); KASSERT(thread != curthread);
KASSERT(thread->t_state != S_RUN); KASSERT(thread->t_state != S_RUN);
/*
* If you add things to struct thread, be sure to clean them up
* either here or in thread_exit(). (And not both...)
*/
/* Thread subsystem fields */ /* Thread subsystem fields */
KASSERT(thread->t_proc == NULL); KASSERT(thread->t_proc == NULL);
if (thread->t_stack != NULL) { if (thread->t_stack != NULL) {
@ -267,7 +286,6 @@ static void thread_destroy(struct thread *thread) {
/* sheer paranoia */ /* sheer paranoia */
thread->t_wchan_name = "DESTROYED"; thread->t_wchan_name = "DESTROYED";
kfree(thread->t_name);
kfree(thread); kfree(thread);
} }
@ -277,7 +295,10 @@ static void thread_destroy(struct thread *thread) {
* *
* The list of zombies is per-cpu. * The list of zombies is per-cpu.
*/ */
static void exorcise(void) { static
void
exorcise(void)
{
struct thread *z; struct thread *z;
while ((z = threadlist_remhead(&curcpu->c_zombies)) != NULL) { while ((z = threadlist_remhead(&curcpu->c_zombies)) != NULL) {
@ -292,7 +313,9 @@ static void exorcise(void) {
* possible) to make sure we don't end up letting any other threads * possible) to make sure we don't end up letting any other threads
* run. * run.
*/ */
void thread_panic(void) { void
thread_panic(void)
{
/* /*
* Kill off other CPUs. * Kill off other CPUs.
* *
@ -332,7 +355,9 @@ void thread_panic(void) {
/* /*
* At system shutdown, ask the other CPUs to switch off. * At system shutdown, ask the other CPUs to switch off.
*/ */
void thread_shutdown(void) { void
thread_shutdown(void)
{
/* /*
* Stop the other CPUs. * Stop the other CPUs.
* *
@ -345,7 +370,9 @@ void thread_shutdown(void) {
/* /*
* Thread system initialization. * Thread system initialization.
*/ */
void thread_bootstrap(void) { void
thread_bootstrap(void)
{
cpuarray_init(&allcpus); cpuarray_init(&allcpus);
/* /*
@ -377,7 +404,9 @@ void thread_bootstrap(void) {
* to do anything. The startup thread can just exit; we only need it * to do anything. The startup thread can just exit; we only need it
* to be able to get into thread_switch() properly. * to be able to get into thread_switch() properly.
*/ */
void cpu_hatch(unsigned software_number) { void
cpu_hatch(unsigned software_number)
{
char buf[64]; char buf[64];
KASSERT(curcpu != NULL); KASSERT(curcpu != NULL);
@ -387,8 +416,6 @@ void cpu_hatch(unsigned software_number) {
spl0(); spl0();
cpu_identify(buf, sizeof(buf)); cpu_identify(buf, sizeof(buf));
kprintf("cpu%u: %s\n", software_number, buf);
V(cpu_startup_sem); V(cpu_startup_sem);
thread_exit(); thread_exit();
} }
@ -396,7 +423,9 @@ void cpu_hatch(unsigned software_number) {
/* /*
* Start up secondary cpus. Called from boot(). * Start up secondary cpus. Called from boot().
*/ */
void thread_start_cpus(void) { void
thread_start_cpus(void)
{
char buf[64]; char buf[64];
unsigned i; unsigned i;
@ -404,13 +433,26 @@ void thread_start_cpus(void) {
kprintf("cpu0: %s\n", buf); kprintf("cpu0: %s\n", buf);
cpu_startup_sem = sem_create("cpu_hatch", 0); cpu_startup_sem = sem_create("cpu_hatch", 0);
thread_count_wchan = wchan_create("thread_count");
mainbus_start_cpus(); mainbus_start_cpus();
for (i = 0; i < cpuarray_num(&allcpus) - 1; i++) { num_cpus = cpuarray_num(&allcpus);
for (i=0; i<num_cpus - 1; i++) {
P(cpu_startup_sem); P(cpu_startup_sem);
} }
sem_destroy(cpu_startup_sem); sem_destroy(cpu_startup_sem);
if (i == 0) {
kprintf("1 CPU online\n");
} else {
kprintf("%d CPUs online\n", i + 1);
}
cpu_startup_sem = NULL; cpu_startup_sem = NULL;
// Gross hack to deal with os/161 "idle" threads. Hardcode the thread count
// to 1 so the inc/dec properly works in thread_[fork/exit]. The one thread
// is the cpu0 boot thread (menu), which is the only thread that hasn't
// exited yet.
thread_count = 1;
} }
/* /*
@ -418,8 +460,10 @@ void thread_start_cpus(void) {
* *
* targetcpu might be curcpu; it might not be, too. * targetcpu might be curcpu; it might not be, too.
*/ */
static void thread_make_runnable(struct thread *target, static
bool already_have_lock) { void
thread_make_runnable(struct thread *target, bool already_have_lock)
{
struct cpu *targetcpu; struct cpu *targetcpu;
/* Lock the run queue of the target thread's cpu. */ /* Lock the run queue of the target thread's cpu. */
@ -428,7 +472,8 @@ static void thread_make_runnable(struct thread *target,
if (already_have_lock) { if (already_have_lock) {
/* The target thread's cpu should be already locked. */ /* The target thread's cpu should be already locked. */
KASSERT(spinlock_do_i_hold(&targetcpu->c_runqueue_lock)); KASSERT(spinlock_do_i_hold(&targetcpu->c_runqueue_lock));
} else { }
else {
spinlock_acquire(&targetcpu->c_runqueue_lock); spinlock_acquire(&targetcpu->c_runqueue_lock);
} }
@ -459,9 +504,12 @@ static void thread_make_runnable(struct thread *target,
* process is inherited from the caller. It will start on the same CPU * process is inherited from the caller. It will start on the same CPU
* as the caller, unless the scheduler intervenes first. * as the caller, unless the scheduler intervenes first.
*/ */
int thread_fork(const char *name, struct proc *proc, int
thread_fork(const char *name,
struct proc *proc,
void (*entrypoint)(void *data1, unsigned long data2), void (*entrypoint)(void *data1, unsigned long data2),
void *data1, unsigned long data2) { void *data1, unsigned long data2)
{
struct thread *newthread; struct thread *newthread;
int result; int result;
@ -503,6 +551,11 @@ int thread_fork(const char *name, struct proc *proc,
*/ */
newthread->t_iplhigh_count++; newthread->t_iplhigh_count++;
spinlock_acquire(&thread_count_lock);
++thread_count;
wchan_wakeall(thread_count_wchan, &thread_count_lock);
spinlock_release(&thread_count_lock);
/* Set up the switchframe so entrypoint() gets called */ /* Set up the switchframe so entrypoint() gets called */
switchframe_init(newthread, entrypoint, data1, data2); switchframe_init(newthread, entrypoint, data1, data2);
@ -522,8 +575,10 @@ int thread_fork(const char *name, struct proc *proc,
* WC, protected by the spinlock LK. Otherwise WC and Lk should be * WC, protected by the spinlock LK. Otherwise WC and Lk should be
* NULL. * NULL.
*/ */
static void thread_switch(threadstate_t newstate, struct wchan *wc, static
struct spinlock *lk) { void
thread_switch(threadstate_t newstate, struct wchan *wc, struct spinlock *lk)
{
struct thread *cur, *next; struct thread *cur, *next;
int spl; int spl;
@ -672,6 +727,7 @@ static void thread_switch(threadstate_t newstate, struct wchan *wc,
* thread_startup. * thread_startup.
*/ */
/* Clear the wait channel and set the thread state. */ /* Clear the wait channel and set the thread state. */
cur->t_wchan_name = NULL; cur->t_wchan_name = NULL;
cur->t_state = S_RUN; cur->t_state = S_RUN;
@ -697,8 +753,10 @@ static void thread_switch(threadstate_t newstate, struct wchan *wc,
* thread_switch, the beginning part of this function must match the * thread_switch, the beginning part of this function must match the
* tail of thread_switch. * tail of thread_switch.
*/ */
void thread_startup(void (*entrypoint)(void *data1, unsigned long data2), void
void *data1, unsigned long data2) { thread_startup(void (*entrypoint)(void *data1, unsigned long data2),
void *data1, unsigned long data2)
{
struct thread *cur; struct thread *cur;
cur = curthread; cur = curthread;
@ -733,9 +791,18 @@ void thread_startup(void (*entrypoint)(void *data1, unsigned long data2),
* should be cleaned up right away. The rest has to wait until * should be cleaned up right away. The rest has to wait until
* thread_destroy is called from exorcise(). * thread_destroy is called from exorcise().
* *
* Note that any dynamically-allocated structures that can vary in size from
* thread to thread should be cleaned up here, not in thread_destroy. This is
* because the last thread left on each core runs the idle loop and does not
* get cleaned up until new threads are created. Differences in the amount of
* memory used by different threads after thread_exit will make it look like
* your kernel in leaking memory and cause some of the test161 checks to fail.
*
* Does not return. * Does not return.
*/ */
void thread_exit(void) { void
thread_exit(void)
{
struct thread *cur; struct thread *cur;
cur = curthread; cur = curthread;
@ -752,6 +819,14 @@ void thread_exit(void) {
/* Check the stack guard band. */ /* Check the stack guard band. */
thread_checkstack(cur); thread_checkstack(cur);
// Decrement the thread count and notify anyone interested.
if (thread_count) {
spinlock_acquire(&thread_count_lock);
--thread_count;
wchan_wakeall(thread_count_wchan, &thread_count_lock);
spinlock_release(&thread_count_lock);
}
/* Interrupts off on this processor */ /* Interrupts off on this processor */
splhigh(); splhigh();
thread_switch(S_ZOMBIE, NULL, NULL); thread_switch(S_ZOMBIE, NULL, NULL);
@ -761,7 +836,11 @@ void thread_exit(void) {
/* /*
* Yield the cpu to another process, but stay runnable. * Yield the cpu to another process, but stay runnable.
*/ */
void thread_yield(void) { thread_switch(S_READY, NULL, NULL); } void
thread_yield(void)
{
thread_switch(S_READY, NULL, NULL);
}
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
@ -772,7 +851,9 @@ void thread_yield(void) { thread_switch(S_READY, NULL, NULL); }
* the current CPU's run queue by job priority. * the current CPU's run queue by job priority.
*/ */
void schedule(void) { void
schedule(void)
{
/* /*
* You can write this. If we do nothing, threads will run in * You can write this. If we do nothing, threads will run in
* round-robin fashion. * round-robin fashion.
@ -796,7 +877,9 @@ void schedule(void) {
* System/161 does not (yet) model such cache effects, we'll be very * System/161 does not (yet) model such cache effects, we'll be very
* aggressive. * aggressive.
*/ */
void thread_consider_migration(void) { void
thread_consider_migration(void)
{
unsigned my_count, total_count, one_share, to_send; unsigned my_count, total_count, one_share, to_send;
unsigned i, numcpus; unsigned i, numcpus;
struct cpu *c; struct cpu *c;
@ -805,7 +888,7 @@ void thread_consider_migration(void) {
my_count = total_count = 0; my_count = total_count = 0;
numcpus = cpuarray_num(&allcpus); numcpus = cpuarray_num(&allcpus);
for (i = 0; i < numcpus; i++) { for (i=0; i<numcpus; i++) {
c = cpuarray_get(&allcpus, i); c = cpuarray_get(&allcpus, i);
spinlock_acquire(&c->c_runqueue_lock); spinlock_acquire(&c->c_runqueue_lock);
total_count += c->c_runqueue.tl_count; total_count += c->c_runqueue.tl_count;
@ -823,13 +906,13 @@ void thread_consider_migration(void) {
to_send = my_count - one_share; to_send = my_count - one_share;
threadlist_init(&victims); threadlist_init(&victims);
spinlock_acquire(&curcpu->c_runqueue_lock); spinlock_acquire(&curcpu->c_runqueue_lock);
for (i = 0; i < to_send; i++) { for (i=0; i<to_send; i++) {
t = threadlist_remtail(&curcpu->c_runqueue); t = threadlist_remtail(&curcpu->c_runqueue);
threadlist_addhead(&victims, t); threadlist_addhead(&victims, t);
} }
spinlock_release(&curcpu->c_runqueue_lock); spinlock_release(&curcpu->c_runqueue_lock);
for (i = 0; i < numcpus && to_send > 0; i++) { for (i=0; i < numcpus && to_send > 0; i++) {
c = cpuarray_get(&allcpus, i); c = cpuarray_get(&allcpus, i);
if (c == curcpu->c_self) { if (c == curcpu->c_self) {
continue; continue;
@ -867,8 +950,9 @@ void thread_consider_migration(void) {
t->t_cpu = c; t->t_cpu = c;
threadlist_addtail(&c->c_runqueue, t); threadlist_addtail(&c->c_runqueue, t);
DEBUG(DB_THREADS, "Migrated thread %s: cpu %u -> %u", t->t_name, DEBUG(DB_THREADS,
curcpu->c_number, c->c_number); "Migrated thread %s: cpu %u -> %u",
t->t_name, curcpu->c_number, c->c_number);
to_send--; to_send--;
if (c->c_isidle) { if (c->c_isidle) {
/* /*
@ -912,7 +996,9 @@ void thread_consider_migration(void) {
* arrangements should be made to free it after the wait channel is * arrangements should be made to free it after the wait channel is
* destroyed. * destroyed.
*/ */
struct wchan *wchan_create(const char *name) { struct wchan *
wchan_create(const char *name)
{
struct wchan *wc; struct wchan *wc;
wc = kmalloc(sizeof(*wc)); wc = kmalloc(sizeof(*wc));
@ -929,7 +1015,9 @@ struct wchan *wchan_create(const char *name) {
* Destroy a wait channel. Must be empty and unlocked. * Destroy a wait channel. Must be empty and unlocked.
* (The corresponding cleanup functions require this.) * (The corresponding cleanup functions require this.)
*/ */
void wchan_destroy(struct wchan *wc) { void
wchan_destroy(struct wchan *wc)
{
threadlist_cleanup(&wc->wc_threads); threadlist_cleanup(&wc->wc_threads);
kfree(wc); kfree(wc);
} }
@ -941,7 +1029,9 @@ void wchan_destroy(struct wchan *wc) {
* be locked. The call to thread_switch unlocks it; we relock it * be locked. The call to thread_switch unlocks it; we relock it
* before returning. * before returning.
*/ */
void wchan_sleep(struct wchan *wc, struct spinlock *lk) { void
wchan_sleep(struct wchan *wc, struct spinlock *lk)
{
/* may not sleep in an interrupt handler */ /* may not sleep in an interrupt handler */
KASSERT(!curthread->t_in_interrupt); KASSERT(!curthread->t_in_interrupt);
@ -958,7 +1048,9 @@ void wchan_sleep(struct wchan *wc, struct spinlock *lk) {
/* /*
* Wake up one thread sleeping on a wait channel. * Wake up one thread sleeping on a wait channel.
*/ */
void wchan_wakeone(struct wchan *wc, struct spinlock *lk) { void
wchan_wakeone(struct wchan *wc, struct spinlock *lk)
{
struct thread *target; struct thread *target;
KASSERT(spinlock_do_i_hold(lk)); KASSERT(spinlock_do_i_hold(lk));
@ -985,7 +1077,9 @@ void wchan_wakeone(struct wchan *wc, struct spinlock *lk) {
/* /*
* Wake up all threads sleeping on a wait channel. * Wake up all threads sleeping on a wait channel.
*/ */
void wchan_wakeall(struct wchan *wc, struct spinlock *lk) { void
wchan_wakeall(struct wchan *wc, struct spinlock *lk)
{
struct thread *target; struct thread *target;
struct threadlist list; struct threadlist list;
@ -1017,7 +1111,9 @@ void wchan_wakeall(struct wchan *wc, struct spinlock *lk) {
* Return nonzero if there are no threads sleeping on the channel. * Return nonzero if there are no threads sleeping on the channel.
* This is meant to be used only for diagnostic purposes. * This is meant to be used only for diagnostic purposes.
*/ */
bool wchan_isempty(struct wchan *wc, struct spinlock *lk) { bool
wchan_isempty(struct wchan *wc, struct spinlock *lk)
{
bool ret; bool ret;
KASSERT(spinlock_do_i_hold(lk)); KASSERT(spinlock_do_i_hold(lk));
@ -1035,7 +1131,9 @@ bool wchan_isempty(struct wchan *wc, struct spinlock *lk) {
/* /*
* Send an IPI (inter-processor interrupt) to the specified CPU. * Send an IPI (inter-processor interrupt) to the specified CPU.
*/ */
void ipi_send(struct cpu *target, int code) { void
ipi_send(struct cpu *target, int code)
{
KASSERT(code >= 0 && code < 32); KASSERT(code >= 0 && code < 32);
spinlock_acquire(&target->c_ipi_lock); spinlock_acquire(&target->c_ipi_lock);
@ -1047,11 +1145,13 @@ void ipi_send(struct cpu *target, int code) {
/* /*
* Send an IPI to all CPUs. * Send an IPI to all CPUs.
*/ */
void ipi_broadcast(int code) { void
ipi_broadcast(int code)
{
unsigned i; unsigned i;
struct cpu *c; struct cpu *c;
for (i = 0; i < cpuarray_num(&allcpus); i++) { for (i=0; i < cpuarray_num(&allcpus); i++) {
c = cpuarray_get(&allcpus, i); c = cpuarray_get(&allcpus, i);
if (c != curcpu->c_self) { if (c != curcpu->c_self) {
ipi_send(c, code); ipi_send(c, code);
@ -1062,7 +1162,9 @@ void ipi_broadcast(int code) {
/* /*
* Send a TLB shootdown IPI to the specified CPU. * Send a TLB shootdown IPI to the specified CPU.
*/ */
void ipi_tlbshootdown(struct cpu *target, const struct tlbshootdown *mapping) { void
ipi_tlbshootdown(struct cpu *target, const struct tlbshootdown *mapping)
{
unsigned n; unsigned n;
spinlock_acquire(&target->c_ipi_lock); spinlock_acquire(&target->c_ipi_lock);
@ -1079,9 +1181,10 @@ void ipi_tlbshootdown(struct cpu *target, const struct tlbshootdown *mapping) {
* reduce the number of unnecessary shootdowns. * reduce the number of unnecessary shootdowns.
*/ */
panic("ipi_tlbshootdown: Too many shootdowns queued\n"); panic("ipi_tlbshootdown: Too many shootdowns queued\n");
} else { }
else {
target->c_shootdown[n] = *mapping; target->c_shootdown[n] = *mapping;
target->c_numshootdown = n + 1; target->c_numshootdown = n+1;
} }
target->c_ipi_pending |= (uint32_t)1 << IPI_TLBSHOOTDOWN; target->c_ipi_pending |= (uint32_t)1 << IPI_TLBSHOOTDOWN;
@ -1093,7 +1196,9 @@ void ipi_tlbshootdown(struct cpu *target, const struct tlbshootdown *mapping) {
/* /*
* Handle an incoming interprocessor interrupt. * Handle an incoming interprocessor interrupt.
*/ */
void interprocessor_interrupt(void) { void
interprocessor_interrupt(void)
{
uint32_t bits; uint32_t bits;
unsigned i; unsigned i;
@ -1110,10 +1215,10 @@ void interprocessor_interrupt(void) {
spinlock_release(&curcpu->c_ipi_lock); spinlock_release(&curcpu->c_ipi_lock);
spinlock_acquire(&curcpu->c_runqueue_lock); spinlock_acquire(&curcpu->c_runqueue_lock);
if (!curcpu->c_isidle) { if (!curcpu->c_isidle) {
kprintf("cpu%d: offline: warning: not idle\n", curcpu->c_number); kprintf("cpu%d: offline: warning: not idle\n",
curcpu->c_number);
} }
spinlock_release(&curcpu->c_runqueue_lock); spinlock_release(&curcpu->c_runqueue_lock);
kprintf("cpu%d: offline.\n", curcpu->c_number);
cpu_halt(); cpu_halt();
} }
if (bits & (1U << IPI_UNIDLE)) { if (bits & (1U << IPI_UNIDLE)) {
@ -1128,7 +1233,7 @@ void interprocessor_interrupt(void) {
* need to release the ipi lock while calling * need to release the ipi lock while calling
* vm_tlbshootdown. * vm_tlbshootdown.
*/ */
for (i = 0; i < curcpu->c_numshootdown; i++) { for (i=0; i<curcpu->c_numshootdown; i++) {
vm_tlbshootdown(&curcpu->c_shootdown[i]); vm_tlbshootdown(&curcpu->c_shootdown[i]);
} }
curcpu->c_numshootdown = 0; curcpu->c_numshootdown = 0;
@ -1137,3 +1242,15 @@ void interprocessor_interrupt(void) {
curcpu->c_ipi_pending = 0; curcpu->c_ipi_pending = 0;
spinlock_release(&curcpu->c_ipi_lock); spinlock_release(&curcpu->c_ipi_lock);
} }
/*
* Wait for the thread count to equal tc.
*/
void thread_wait_for_count(unsigned tc)
{
spinlock_acquire(&thread_count_lock);
while (thread_count != tc) {
wchan_sleep(thread_count_wchan, &thread_count_lock);
}
spinlock_release(&thread_count_lock);
}

View File

@ -31,19 +31,25 @@
#include <lib.h> #include <lib.h>
#include <spinlock.h> #include <spinlock.h>
#include <vm.h> #include <vm.h>
#include <kern/test161.h>
#include <test.h>
/* /*
* Kernel malloc. * Kernel malloc.
*/ */
/* /*
* Fill a block with 0xdeadbeef. * Fill a block with 0xdeadbeef.
*/ */
static void fill_deadbeef(void *vptr, size_t len) { static
void
fill_deadbeef(void *vptr, size_t len)
{
uint32_t *ptr = vptr; uint32_t *ptr = vptr;
size_t i; size_t i;
for (i = 0; i < len / sizeof(uint32_t); i++) { for (i=0; i<len/sizeof(uint32_t); i++) {
ptr[i] = 0xdeadbeef; ptr[i] = 0xdeadbeef;
} }
} }
@ -122,7 +128,7 @@ static void fill_deadbeef(void *vptr, size_t len) {
#if PAGE_SIZE == 4096 #if PAGE_SIZE == 4096
#define NSIZES 8 #define NSIZES 8
static const size_t sizes[NSIZES] = {16, 32, 64, 128, 256, 512, 1024, 2048}; static const size_t sizes[NSIZES] = { 16, 32, 64, 128, 256, 512, 1024, 2048 };
#define SMALLEST_SUBPAGE_SIZE 16 #define SMALLEST_SUBPAGE_SIZE 16
#define LARGEST_SUBPAGE_SIZE 2048 #define LARGEST_SUBPAGE_SIZE 2048
@ -151,7 +157,7 @@ struct pageref {
#define PR_PAGEADDR(pr) ((pr)->pageaddr_and_blocktype & PAGE_FRAME) #define PR_PAGEADDR(pr) ((pr)->pageaddr_and_blocktype & PAGE_FRAME)
#define PR_BLOCKTYPE(pr) ((pr)->pageaddr_and_blocktype & ~PAGE_FRAME) #define PR_BLOCKTYPE(pr) ((pr)->pageaddr_and_blocktype & ~PAGE_FRAME)
#define MKPAB(pa, blk) (((pa) & PAGE_FRAME) | ((blk) & ~PAGE_FRAME)) #define MKPAB(pa, blk) (((pa)&PAGE_FRAME) | ((blk) & ~PAGE_FRAME))
//////////////////////////////////////// ////////////////////////////////////////
@ -211,7 +217,10 @@ static struct kheap_root kheaproots[NUM_PAGEREFPAGES];
/* /*
* Allocate a page to hold pagerefs. * Allocate a page to hold pagerefs.
*/ */
static void allocpagerefpage(struct kheap_root *root) { static
void
allocpagerefpage(struct kheap_root *root)
{
vaddr_t va; vaddr_t va;
KASSERT(root->page == NULL); KASSERT(root->page == NULL);
@ -246,13 +255,16 @@ static void allocpagerefpage(struct kheap_root *root) {
/* /*
* Allocate a pageref structure. * Allocate a pageref structure.
*/ */
static struct pageref *allocpageref(void) { static
unsigned i, j; struct pageref *
allocpageref(void)
{
unsigned i,j;
uint32_t k; uint32_t k;
unsigned whichroot; unsigned whichroot;
struct kheap_root *root; struct kheap_root *root;
for (whichroot = 0; whichroot < NUM_PAGEREFPAGES; whichroot++) { for (whichroot=0; whichroot < NUM_PAGEREFPAGES; whichroot++) {
root = &kheaproots[whichroot]; root = &kheaproots[whichroot];
if (root->numinuse >= NPAGEREFS_PER_PAGE) { if (root->numinuse >= NPAGEREFS_PER_PAGE) {
continue; continue;
@ -261,13 +273,13 @@ static struct pageref *allocpageref(void) {
/* /*
* This should probably not be a linear search. * This should probably not be a linear search.
*/ */
for (i = 0; i < INUSE_WORDS; i++) { for (i=0; i<INUSE_WORDS; i++) {
if (root->pagerefs_inuse[i] == 0xffffffff) { if (root->pagerefs_inuse[i]==0xffffffff) {
/* full */ /* full */
continue; continue;
} }
for (k = 1, j = 0; k != 0; k <<= 1, j++) { for (k=1,j=0; k!=0; k<<=1,j++) {
if ((root->pagerefs_inuse[i] & k) == 0) { if ((root->pagerefs_inuse[i] & k)==0) {
root->pagerefs_inuse[i] |= k; root->pagerefs_inuse[i] |= k;
root->numinuse++; root->numinuse++;
if (root->page == NULL) { if (root->page == NULL) {
@ -276,7 +288,7 @@ static struct pageref *allocpageref(void) {
if (root->page == NULL) { if (root->page == NULL) {
return NULL; return NULL;
} }
return &root->page->refs[i * 32 + j]; return &root->page->refs[i*32 + j];
} }
} }
KASSERT(0); KASSERT(0);
@ -290,14 +302,17 @@ static struct pageref *allocpageref(void) {
/* /*
* Release a pageref structure. * Release a pageref structure.
*/ */
static void freepageref(struct pageref *p) { static
void
freepageref(struct pageref *p)
{
size_t i, j; size_t i, j;
uint32_t k; uint32_t k;
unsigned whichroot; unsigned whichroot;
struct kheap_root *root; struct kheap_root *root;
struct pagerefpage *page; struct pagerefpage *page;
for (whichroot = 0; whichroot < NUM_PAGEREFPAGES; whichroot++) { for (whichroot=0; whichroot < NUM_PAGEREFPAGES; whichroot++) {
root = &kheaproots[whichroot]; root = &kheaproots[whichroot];
page = root->page; page = root->page;
@ -306,12 +321,12 @@ static void freepageref(struct pageref *p) {
continue; continue;
} }
j = p - page->refs; j = p-page->refs;
/* note: j is unsigned, don't test < 0 */ /* note: j is unsigned, don't test < 0 */
if (j < NPAGEREFS_PER_PAGE) { if (j < NPAGEREFS_PER_PAGE) {
/* on this page */ /* on this page */
i = j / 32; i = j/32;
k = ((uint32_t)1) << (j % 32); k = ((uint32_t)1) << (j%32);
KASSERT((root->pagerefs_inuse[i] & k) != 0); KASSERT((root->pagerefs_inuse[i] & k) != 0);
root->pagerefs_inuse[i] &= ~k; root->pagerefs_inuse[i] &= ~k;
KASSERT(root->numinuse > 0); KASSERT(root->numinuse > 0);
@ -352,8 +367,10 @@ static struct pageref *allbase;
/* /*
* Set up the guard values in a block we're about to return. * Set up the guard values in a block we're about to return.
*/ */
static void *establishguardband(void *block, size_t clientsize, static
size_t blocksize) { void *
establishguardband(void *block, size_t clientsize, size_t blocksize)
{
vaddr_t lowguard, lowsize, data, enddata, highguard, highsize, i; vaddr_t lowguard, lowsize, data, enddata, highguard, highsize, i;
KASSERT(clientsize + GUARD_OVERHEAD <= blocksize); KASSERT(clientsize + GUARD_OVERHEAD <= blocksize);
@ -368,10 +385,10 @@ static void *establishguardband(void *block, size_t clientsize,
*(uint16_t *)lowguard = GUARD_HALFWORD; *(uint16_t *)lowguard = GUARD_HALFWORD;
*(uint16_t *)lowsize = clientsize; *(uint16_t *)lowsize = clientsize;
for (i = data; i < enddata; i++) { for (i=data; i<enddata; i++) {
*(uint8_t *)i = GUARD_RETBYTE; *(uint8_t *)i = GUARD_RETBYTE;
} }
for (i = enddata; i < highguard; i++) { for (i=enddata; i<highguard; i++) {
*(uint8_t *)i = GUARD_FILLBYTE; *(uint8_t *)i = GUARD_FILLBYTE;
} }
*(uint16_t *)highguard = GUARD_HALFWORD; *(uint16_t *)highguard = GUARD_HALFWORD;
@ -383,8 +400,10 @@ static void *establishguardband(void *block, size_t clientsize,
/* /*
* Validate the guard values in an existing block. * Validate the guard values in an existing block.
*/ */
static void checkguardband(vaddr_t blockaddr, size_t smallerblocksize, static
size_t blocksize) { void
checkguardband(vaddr_t blockaddr, size_t smallerblocksize, size_t blocksize)
{
/* /*
* The first two bytes of the block are the lower guard band. * The first two bytes of the block are the lower guard band.
* The next two bytes are the real size (the size of the * The next two bytes are the real size (the size of the
@ -413,7 +432,7 @@ static void checkguardband(vaddr_t blockaddr, size_t smallerblocksize,
KASSERT(clientsize + GUARD_OVERHEAD > smallerblocksize); KASSERT(clientsize + GUARD_OVERHEAD > smallerblocksize);
KASSERT(clientsize + GUARD_OVERHEAD <= blocksize); KASSERT(clientsize + GUARD_OVERHEAD <= blocksize);
enddata = data + clientsize; enddata = data + clientsize;
for (i = enddata; i < highguard; i++) { for (i=enddata; i<highguard; i++) {
KASSERT(*(uint8_t *)i == GUARD_FILLBYTE); KASSERT(*(uint8_t *)i == GUARD_FILLBYTE);
} }
} }
@ -440,11 +459,14 @@ static void checkguardband(vaddr_t blockaddr, size_t smallerblocksize,
* The first word of the block is a freelist pointer and should not be * The first word of the block is a freelist pointer and should not be
* deadbeef; the rest of the block should be only deadbeef. * deadbeef; the rest of the block should be only deadbeef.
*/ */
static void checkdeadbeef(void *block, size_t blocksize) { static
void
checkdeadbeef(void *block, size_t blocksize)
{
uint32_t *ptr = block; uint32_t *ptr = block;
size_t i; size_t i;
for (i = 1; i < blocksize / sizeof(uint32_t); i++) { for (i=1; i < blocksize/sizeof(uint32_t); i++) {
KASSERT(ptr[i] == 0xdeadbeef); KASSERT(ptr[i] == 0xdeadbeef);
} }
} }
@ -470,11 +492,14 @@ static void checkdeadbeef(void *block, size_t blocksize) {
* assertion as a bit in isfree is set twice; if not, a circular * assertion as a bit in isfree is set twice; if not, a circular
* freelist will cause an infinite loop. * freelist will cause an infinite loop.
*/ */
static void checksubpage(struct pageref *pr) { static
void
checksubpage(struct pageref *pr)
{
vaddr_t prpage, fla; vaddr_t prpage, fla;
struct freelist *fl; struct freelist *fl;
int blktype; int blktype;
int nfree = 0; int nfree=0;
size_t blocksize; size_t blocksize;
#ifdef CHECKGUARDS #ifdef CHECKGUARDS
const unsigned maxblocks = PAGE_SIZE / SMALLEST_SUBPAGE_SIZE; const unsigned maxblocks = PAGE_SIZE / SMALLEST_SUBPAGE_SIZE;
@ -487,7 +512,7 @@ static void checksubpage(struct pageref *pr) {
KASSERT(spinlock_do_i_hold(&kmalloc_spinlock)); KASSERT(spinlock_do_i_hold(&kmalloc_spinlock));
if (pr->freelist_offset == INVALID_OFFSET) { if (pr->freelist_offset == INVALID_OFFSET) {
KASSERT(pr->nfree == 0); KASSERT(pr->nfree==0);
return; return;
} }
@ -498,7 +523,7 @@ static void checksubpage(struct pageref *pr) {
#ifdef CHECKGUARDS #ifdef CHECKGUARDS
smallerblocksize = blktype > 0 ? sizes[blktype - 1] : 0; smallerblocksize = blktype > 0 ? sizes[blktype - 1] : 0;
for (i = 0; i < numfreewords; i++) { for (i=0; i<numfreewords; i++) {
isfree[i] = 0; isfree[i] = 0;
} }
#endif #endif
@ -517,12 +542,12 @@ static void checksubpage(struct pageref *pr) {
for (; fl != NULL; fl = fl->next) { for (; fl != NULL; fl = fl->next) {
fla = (vaddr_t)fl; fla = (vaddr_t)fl;
KASSERT(fla >= prpage && fla < prpage + PAGE_SIZE); KASSERT(fla >= prpage && fla < prpage + PAGE_SIZE);
KASSERT((fla - prpage) % blocksize == 0); KASSERT((fla-prpage) % blocksize == 0);
#ifdef CHECKBEEF #ifdef CHECKBEEF
checkdeadbeef(fl, blocksize); checkdeadbeef(fl, blocksize);
#endif #endif
#ifdef CHECKGUARDS #ifdef CHECKGUARDS
blocknum = (fla - prpage) / blocksize; blocknum = (fla-prpage) / blocksize;
mask = 1U << (blocknum % 32); mask = 1U << (blocknum % 32);
KASSERT((isfree[blocknum / 32] & mask) == 0); KASSERT((isfree[blocknum / 32] & mask) == 0);
isfree[blocknum / 32] |= mask; isfree[blocknum / 32] |= mask;
@ -530,14 +555,15 @@ static void checksubpage(struct pageref *pr) {
KASSERT(fl->next != fl); KASSERT(fl->next != fl);
nfree++; nfree++;
} }
KASSERT(nfree == pr->nfree); KASSERT(nfree==pr->nfree);
#ifdef CHECKGUARDS #ifdef CHECKGUARDS
numblocks = PAGE_SIZE / blocksize; numblocks = PAGE_SIZE / blocksize;
for (i = 0; i < numblocks; i++) { for (i=0; i<numblocks; i++) {
mask = 1U << (i % 32); mask = 1U << (i % 32);
if ((isfree[i / 32] & mask) == 0) { if ((isfree[i / 32] & mask) == 0) {
checkguardband(prpage + i * blocksize, smallerblocksize, blocksize); checkguardband(prpage + i * blocksize,
smallerblocksize, blocksize);
} }
} }
#endif #endif
@ -551,14 +577,17 @@ static void checksubpage(struct pageref *pr) {
* Run checksubpage on all heap pages. This also checks that the * Run checksubpage on all heap pages. This also checks that the
* linked lists of pagerefs are more or less intact. * linked lists of pagerefs are more or less intact.
*/ */
static void checksubpages(void) { static
void
checksubpages(void)
{
struct pageref *pr; struct pageref *pr;
int i; int i;
unsigned sc = 0, ac = 0; unsigned sc=0, ac=0;
KASSERT(spinlock_do_i_hold(&kmalloc_spinlock)); KASSERT(spinlock_do_i_hold(&kmalloc_spinlock));
for (i = 0; i < NSIZES; i++) { for (i=0; i<NSIZES; i++) {
for (pr = sizebases[i]; pr != NULL; pr = pr->next_samesize) { for (pr = sizebases[i]; pr != NULL; pr = pr->next_samesize) {
checksubpage(pr); checksubpage(pr);
KASSERT(sc < TOTAL_PAGEREFS); KASSERT(sc < TOTAL_PAGEREFS);
@ -572,7 +601,7 @@ static void checksubpages(void) {
ac++; ac++;
} }
KASSERT(sc == ac); KASSERT(sc==ac);
} }
#else #else
#define checksubpages() #define checksubpages()
@ -595,7 +624,10 @@ static unsigned mallocgeneration;
/* /*
* Label a block of memory. * Label a block of memory.
*/ */
static void *establishlabel(void *block, vaddr_t label) { static
void *
establishlabel(void *block, vaddr_t label)
{
struct malloclabel *ml; struct malloclabel *ml;
ml = block; ml = block;
@ -605,7 +637,10 @@ static void *establishlabel(void *block, vaddr_t label) {
return ml; return ml;
} }
static void dump_subpage(struct pageref *pr, unsigned generation) { static
void
dump_subpage(struct pageref *pr, unsigned generation)
{
unsigned blocksize = sizes[PR_BLOCKTYPE(pr)]; unsigned blocksize = sizes[PR_BLOCKTYPE(pr)];
unsigned numblocks = PAGE_SIZE / blocksize; unsigned numblocks = PAGE_SIZE / blocksize;
unsigned numfreewords = DIVROUNDUP(numblocks, 32); unsigned numfreewords = DIVROUNDUP(numblocks, 32);
@ -616,7 +651,7 @@ static void dump_subpage(struct pageref *pr, unsigned generation) {
struct malloclabel *ml; struct malloclabel *ml;
unsigned i; unsigned i;
for (i = 0; i < numfreewords; i++) { for (i=0; i<numfreewords; i++) {
isfree[i] = 0; isfree[i] = 0;
} }
@ -628,7 +663,7 @@ static void dump_subpage(struct pageref *pr, unsigned generation) {
isfree[i / 32] |= mask; isfree[i / 32] |= mask;
} }
for (i = 0; i < numblocks; i++) { for (i=0; i<numblocks; i++) {
mask = 1U << (i % 32); mask = 1U << (i % 32);
if (isfree[i / 32] & mask) { if (isfree[i / 32] & mask) {
continue; continue;
@ -638,17 +673,20 @@ static void dump_subpage(struct pageref *pr, unsigned generation) {
if (ml->generation != generation) { if (ml->generation != generation) {
continue; continue;
} }
kprintf("%5zu bytes at %p, allocated at %p\n", blocksize, (void *)blockaddr, kprintf("%5zu bytes at %p, allocated at %p\n",
(void *)ml->label); blocksize, (void *)blockaddr, (void *)ml->label);
} }
} }
static void dump_subpages(unsigned generation) { static
void
dump_subpages(unsigned generation)
{
struct pageref *pr; struct pageref *pr;
int i; int i;
kprintf("Remaining allocations from generation %u:\n", generation); kprintf("Remaining allocations from generation %u:\n", generation);
for (i = 0; i < NSIZES; i++) { for (i=0; i<NSIZES; i++) {
for (pr = sizebases[i]; pr != NULL; pr = pr->next_samesize) { for (pr = sizebases[i]; pr != NULL; pr = pr->next_samesize) {
dump_subpage(pr, generation); dump_subpage(pr, generation);
} }
@ -661,7 +699,9 @@ static void dump_subpages(unsigned generation) {
#endif /* LABELS */ #endif /* LABELS */
void kheap_nextgeneration(void) { void
kheap_nextgeneration(void)
{
#ifdef LABELS #ifdef LABELS
spinlock_acquire(&kmalloc_spinlock); spinlock_acquire(&kmalloc_spinlock);
mallocgeneration++; mallocgeneration++;
@ -669,7 +709,9 @@ void kheap_nextgeneration(void) {
#endif #endif
} }
void kheap_dump(void) { void
kheap_dump(void)
{
#ifdef LABELS #ifdef LABELS
/* print the whole thing with interrupts off */ /* print the whole thing with interrupts off */
spinlock_acquire(&kmalloc_spinlock); spinlock_acquire(&kmalloc_spinlock);
@ -680,13 +722,15 @@ void kheap_dump(void) {
#endif #endif
} }
void kheap_dumpall(void) { void
kheap_dumpall(void)
{
#ifdef LABELS #ifdef LABELS
unsigned i; unsigned i;
/* print the whole thing with interrupts off */ /* print the whole thing with interrupts off */
spinlock_acquire(&kmalloc_spinlock); spinlock_acquire(&kmalloc_spinlock);
for (i = 0; i <= mallocgeneration; i++) { for (i=0; i<=mallocgeneration; i++) {
dump_subpages(i); dump_subpages(i);
} }
spinlock_release(&kmalloc_spinlock); spinlock_release(&kmalloc_spinlock);
@ -700,18 +744,21 @@ void kheap_dumpall(void) {
/* /*
* Print the allocated/freed map of a single kernel heap page. * Print the allocated/freed map of a single kernel heap page.
*/ */
static void subpage_stats(struct pageref *pr) { static
unsigned long
subpage_stats(struct pageref *pr, bool quiet)
{
vaddr_t prpage, fla; vaddr_t prpage, fla;
struct freelist *fl; struct freelist *fl;
int blktype; int blktype;
unsigned i, n, index; unsigned i, n, index;
uint32_t freemap[PAGE_SIZE / (SMALLEST_SUBPAGE_SIZE * 32)]; uint32_t freemap[PAGE_SIZE / (SMALLEST_SUBPAGE_SIZE*32)];
checksubpage(pr); checksubpage(pr);
KASSERT(spinlock_do_i_hold(&kmalloc_spinlock)); KASSERT(spinlock_do_i_hold(&kmalloc_spinlock));
/* clear freemap[] */ /* clear freemap[] */
for (i = 0; i < ARRAYCOUNT(freemap); i++) { for (i=0; i<ARRAYCOUNT(freemap); i++) {
freemap[i] = 0; freemap[i] = 0;
} }
@ -729,29 +776,35 @@ static void subpage_stats(struct pageref *pr) {
for (; fl != NULL; fl = fl->next) { for (; fl != NULL; fl = fl->next) {
fla = (vaddr_t)fl; fla = (vaddr_t)fl;
index = (fla - prpage) / sizes[blktype]; index = (fla-prpage) / sizes[blktype];
KASSERT(index < n); KASSERT(index<n);
freemap[index / 32] |= (1 << (index % 32)); freemap[index/32] |= (1<<(index%32));
} }
} }
kprintf("at 0x%08lx: size %-4lu %u/%u free\n", (unsigned long)prpage, if (!quiet) {
(unsigned long)sizes[blktype], (unsigned)pr->nfree, n); kprintf("at 0x%08lx: size %-4lu %u/%u free\n",
(unsigned long)prpage, (unsigned long) sizes[blktype],
(unsigned) pr->nfree, n);
kprintf(" "); kprintf(" ");
for (i = 0; i < n; i++) { for (i=0; i<n; i++) {
int val = (freemap[i / 32] & (1 << (i % 32))) != 0; int val = (freemap[i/32] & (1<<(i%32)))!=0;
kprintf("%c", val ? '.' : '*'); kprintf("%c", val ? '.' : '*');
if (i % 64 == 63 && i < n - 1) { if (i%64==63 && i<n-1) {
kprintf("\n "); kprintf("\n ");
} }
} }
kprintf("\n"); kprintf("\n");
}
return ((unsigned long)sizes[blktype] * (n - (unsigned) pr->nfree));
} }
/* /*
* Print the whole heap. * Print the whole heap.
*/ */
void kheap_printstats(void) { void
kheap_printstats(void)
{
struct pageref *pr; struct pageref *pr;
/* print the whole thing with interrupts off */ /* print the whole thing with interrupts off */
@ -760,21 +813,67 @@ void kheap_printstats(void) {
kprintf("Subpage allocator status:\n"); kprintf("Subpage allocator status:\n");
for (pr = allbase; pr != NULL; pr = pr->next_all) { for (pr = allbase; pr != NULL; pr = pr->next_all) {
subpage_stats(pr); subpage_stats(pr, false);
} }
spinlock_release(&kmalloc_spinlock); spinlock_release(&kmalloc_spinlock);
} }
/*
* Return the number of used bytes.
*/
unsigned long
kheap_getused(void) {
struct pageref *pr;
unsigned long total = 0;
unsigned int num_pages = 0, coremap_bytes = 0;
/* compute with interrupts off */
spinlock_acquire(&kmalloc_spinlock);
for (pr = allbase; pr != NULL; pr = pr->next_all) {
total += subpage_stats(pr, true);
num_pages++;
}
coremap_bytes = coremap_used_bytes();
// Don't double-count the pages we're using for subpage allocation;
// we've already accounted for the used portion.
if (coremap_bytes > 0) {
total += coremap_bytes - (num_pages * PAGE_SIZE);
}
spinlock_release(&kmalloc_spinlock);
return total;
}
/*
* Print number of used bytes.
*/
void
kheap_printused(void)
{
char total_string[32];
snprintf(total_string, sizeof(total_string), "%lu", kheap_getused());
secprintf(SECRET, total_string, "khu");
}
//////////////////////////////////////// ////////////////////////////////////////
/* /*
* Remove a pageref from both lists that it's on. * Remove a pageref from both lists that it's on.
*/ */
static void remove_lists(struct pageref *pr, int blktype) { static
void
remove_lists(struct pageref *pr, int blktype)
{
struct pageref **guy; struct pageref **guy;
KASSERT(blktype >= 0 && blktype < NSIZES); KASSERT(blktype>=0 && blktype<NSIZES);
for (guy = &sizebases[blktype]; *guy; guy = &(*guy)->next_samesize) { for (guy = &sizebases[blktype]; *guy; guy = &(*guy)->next_samesize) {
checksubpage(*guy); checksubpage(*guy);
@ -797,15 +896,19 @@ static void remove_lists(struct pageref *pr, int blktype) {
* Given a requested client size, return the block type, that is, the * Given a requested client size, return the block type, that is, the
* index into the sizes[] array for the block size to use. * index into the sizes[] array for the block size to use.
*/ */
static inline int blocktype(size_t clientsz) { static
inline
int blocktype(size_t clientsz)
{
unsigned i; unsigned i;
for (i = 0; i < NSIZES; i++) { for (i=0; i<NSIZES; i++) {
if (clientsz <= sizes[i]) { if (clientsz <= sizes[i]) {
return i; return i;
} }
} }
panic("Subpage allocator cannot handle allocation of size %zu\n", clientsz); panic("Subpage allocator cannot handle allocation of size %zu\n",
clientsz);
// keep compiler happy // keep compiler happy
return 0; return 0;
@ -815,12 +918,14 @@ static inline int blocktype(size_t clientsz) {
* Allocate a block of size SZ, where SZ is not large enough to * Allocate a block of size SZ, where SZ is not large enough to
* warrant a whole-page allocation. * warrant a whole-page allocation.
*/ */
static void *subpage_kmalloc(size_t sz static
void *
subpage_kmalloc(size_t sz
#ifdef LABELS #ifdef LABELS
, , vaddr_t label
vaddr_t label
#endif #endif
) { )
{
unsigned blktype; // index into sizes[] that we're using unsigned blktype; // index into sizes[] that we're using
struct pageref *pr; // pageref for page we're allocating from struct pageref *pr; // pageref for page we're allocating from
vaddr_t prpage; // PR_PAGEADDR(pr) vaddr_t prpage; // PR_PAGEADDR(pr)
@ -878,7 +983,8 @@ static void *subpage_kmalloc(size_t sz
fla = (vaddr_t)fl; fla = (vaddr_t)fl;
KASSERT(fla - prpage < PAGE_SIZE); KASSERT(fla - prpage < PAGE_SIZE);
pr->freelist_offset = fla - prpage; pr->freelist_offset = fla - prpage;
} else { }
else {
KASSERT(pr->nfree == 0); KASSERT(pr->nfree == 0);
pr->freelist_offset = INVALID_OFFSET; pr->freelist_offset = INVALID_OFFSET;
} }
@ -907,9 +1013,9 @@ static void *subpage_kmalloc(size_t sz
spinlock_release(&kmalloc_spinlock); spinlock_release(&kmalloc_spinlock);
prpage = alloc_kpages(1); prpage = alloc_kpages(1);
if (prpage == 0) { if (prpage==0) {
/* Out of memory. */ /* Out of memory. */
kprintf("kmalloc: Subpage allocator couldn't get a page\n"); silent("kmalloc: Subpage allocator couldn't get a page\n");
return NULL; return NULL;
} }
KASSERT(prpage % PAGE_SIZE == 0); KASSERT(prpage % PAGE_SIZE == 0);
@ -920,7 +1026,7 @@ static void *subpage_kmalloc(size_t sz
spinlock_acquire(&kmalloc_spinlock); spinlock_acquire(&kmalloc_spinlock);
pr = allocpageref(); pr = allocpageref();
if (pr == NULL) { if (pr==NULL) {
/* Couldn't allocate accounting space for the new page. */ /* Couldn't allocate accounting space for the new page. */
spinlock_release(&kmalloc_spinlock); spinlock_release(&kmalloc_spinlock);
free_kpages(prpage); free_kpages(prpage);
@ -940,14 +1046,14 @@ static void *subpage_kmalloc(size_t sz
fla = prpage; fla = prpage;
fl = (struct freelist *)fla; fl = (struct freelist *)fla;
fl->next = NULL; fl->next = NULL;
for (i = 1; i < pr->nfree; i++) { for (i=1; i<pr->nfree; i++) {
fl = (struct freelist *)(fla + i * sizes[blktype]); fl = (struct freelist *)(fla + i*sizes[blktype]);
fl->next = (struct freelist *)(fla + (i - 1) * sizes[blktype]); fl->next = (struct freelist *)(fla + (i-1)*sizes[blktype]);
KASSERT(fl != fl->next); KASSERT(fl != fl->next);
} }
fla = (vaddr_t)fl; fla = (vaddr_t) fl;
pr->freelist_offset = fla - prpage; pr->freelist_offset = fla - prpage;
KASSERT(pr->freelist_offset == (pr->nfree - 1) * sizes[blktype]); KASSERT(pr->freelist_offset == (pr->nfree-1)*sizes[blktype]);
pr->next_samesize = sizebases[blktype]; pr->next_samesize = sizebases[blktype];
sizebases[blktype] = pr; sizebases[blktype] = pr;
@ -963,7 +1069,10 @@ static void *subpage_kmalloc(size_t sz
* Free a pointer previously returned from subpage_kmalloc. If the * Free a pointer previously returned from subpage_kmalloc. If the
* pointer is not on any heap page we recognize, return -1. * pointer is not on any heap page we recognize, return -1.
*/ */
static int subpage_kfree(void *ptr) { static
int
subpage_kfree(void *ptr)
{
int blktype; // index into sizes[] that we're using int blktype; // index into sizes[] that we're using
vaddr_t ptraddr; // same as ptr vaddr_t ptraddr; // same as ptr
struct pageref *pr; // pageref for page we're freeing in struct pageref *pr; // pageref for page we're freeing in
@ -1014,7 +1123,7 @@ static int subpage_kfree(void *ptr) {
KASSERT(blktype >= 0 && blktype < NSIZES); KASSERT(blktype >= 0 && blktype < NSIZES);
/* check for corruption */ /* check for corruption */
KASSERT(blktype >= 0 && blktype < NSIZES); KASSERT(blktype>=0 && blktype<NSIZES);
checksubpage(pr); checksubpage(pr);
if (ptraddr >= prpage && ptraddr < prpage + PAGE_SIZE) { if (ptraddr >= prpage && ptraddr < prpage + PAGE_SIZE) {
@ -1022,7 +1131,7 @@ static int subpage_kfree(void *ptr) {
} }
} }
if (pr == NULL) { if (pr==NULL) {
/* Not on any of our pages - not a subpage allocation */ /* Not on any of our pages - not a subpage allocation */
spinlock_release(&kmalloc_spinlock); spinlock_release(&kmalloc_spinlock);
return -1; return -1;
@ -1084,7 +1193,8 @@ static int subpage_kfree(void *ptr) {
/* Call free_kpages without kmalloc_spinlock. */ /* Call free_kpages without kmalloc_spinlock. */
spinlock_release(&kmalloc_spinlock); spinlock_release(&kmalloc_spinlock);
free_kpages(prpage); free_kpages(prpage);
} else { }
else {
spinlock_release(&kmalloc_spinlock); spinlock_release(&kmalloc_spinlock);
} }
@ -1104,7 +1214,9 @@ static int subpage_kfree(void *ptr) {
* Allocate a block of size SZ. Redirect either to subpage_kmalloc or * Allocate a block of size SZ. Redirect either to subpage_kmalloc or
* alloc_kpages depending on how big SZ is. * alloc_kpages depending on how big SZ is.
*/ */
void *kmalloc(size_t sz) { void *
kmalloc(size_t sz)
{
size_t checksz; size_t checksz;
#ifdef LABELS #ifdef LABELS
vaddr_t label; vaddr_t label;
@ -1124,9 +1236,9 @@ void *kmalloc(size_t sz) {
vaddr_t address; vaddr_t address;
/* Round up to a whole number of pages. */ /* Round up to a whole number of pages. */
npages = (sz + PAGE_SIZE - 1) / PAGE_SIZE; npages = (sz + PAGE_SIZE - 1)/PAGE_SIZE;
address = alloc_kpages(npages); address = alloc_kpages(npages);
if (address == 0) { if (address==0) {
return NULL; return NULL;
} }
KASSERT(address % PAGE_SIZE == 0); KASSERT(address % PAGE_SIZE == 0);
@ -1144,14 +1256,17 @@ void *kmalloc(size_t sz) {
/* /*
* Free a block previously returned from kmalloc. * Free a block previously returned from kmalloc.
*/ */
void kfree(void *ptr) { void
kfree(void *ptr)
{
/* /*
* Try subpage first; if that fails, assume it's a big allocation. * Try subpage first; if that fails, assume it's a big allocation.
*/ */
if (ptr == NULL) { if (ptr == NULL) {
return; return;
} else if (subpage_kfree(ptr)) { } else if (subpage_kfree(ptr)) {
KASSERT((vaddr_t)ptr % PAGE_SIZE == 0); KASSERT((vaddr_t)ptr%PAGE_SIZE==0);
free_kpages((vaddr_t)ptr); free_kpages((vaddr_t)ptr);
} }
} }

View File

@ -49,7 +49,7 @@ Standard C Library (libc, -lc)
<p> <p>
<tt>#include &lt;unistd.h&gt;</tt><br> <tt>#include &lt;unistd.h&gt;</tt><br>
<br> <br>
<tt>void,</tt><br> <tt>void</tt><br>
<tt>_exit(int </tt><em>exitcode</em><tt>);</tt> <tt>_exit(int </tt><em>exitcode</em><tt>);</tt>
</p> </p>

View File

@ -55,8 +55,8 @@ Standard C Library (libc, -lc)
<h3>Description</h3> <h3>Description</h3>
<p> <p>
The file handle <em>fd</em> is closed. The same file handle may then The file handle identified by file descriptor <em>fd</em> is closed.
be returned again from <A HREF=open.html>open</A>, The same file handle may then be returned again from <A HREF=open.html>open</A>,
<A HREF=dup2.html>dup2</A>, <A HREF=pipe.html>pipe</A>, or similar <A HREF=dup2.html>dup2</A>, <A HREF=pipe.html>pipe</A>, or similar
calls. calls.
</p> </p>
@ -87,7 +87,7 @@ mentioned here.
<table width=90%> <table width=90%>
<tr><td width=5% rowspan=10>&nbsp;</td> <tr><td width=5% rowspan=10>&nbsp;</td>
<td width=10%>EBADF</td> <td width=10%>EBADF</td>
<td><em>fd</em> is not a valid file handle.</td></tr> <td><em>fd</em> is not a valid file descriptor.</td></tr>
<tr><td>EIO</td> <td>A hard I/O error occurred.</td></tr> <tr><td>EIO</td> <td>A hard I/O error occurred.</td></tr>
</table> </table>
</p> </p>

View File

@ -55,9 +55,9 @@ Standard C Library (libc, -lc)
<h3>Description</h3> <h3>Description</h3>
<p> <p>
<tt>dup2</tt> clones the file handle <em>oldfd</em> onto the file <tt>dup2</tt> clones the file handle identifed by file descriptor <em>oldfd</em>
handle <em>newfd</em>. If <em>newfd</em> names an already-open file, onto the file handle identified by <em>newfd</em>. If <em>newfd</em>
that file is closed. names an already-open file, that file is closed.
</p> </p>
<p> <p>
@ -74,8 +74,8 @@ dup2 is most commonly used to relocate opened files onto
</p> </p>
<p> <p>
Both filehandles must be non-negative, and, if applicable, smaller Both file descriptors must be non-negative, and, if applicable,
than the maximum allowed file handle number. smaller than the maximum allowed file handle number.
</p> </p>
<p> <p>
@ -116,9 +116,9 @@ here.
<tr><td width=5% rowspan=3>&nbsp;</td> <tr><td width=5% rowspan=3>&nbsp;</td>
<td width=10% valign=top>EBADF</td> <td width=10% valign=top>EBADF</td>
<td><em>oldfd</em> is not a valid file <td><em>oldfd</em> is not a valid file
handle, or <em>newfd</em> is a value descriptor, or <em>newfd</em> is a value
that cannot be a valid file that cannot be a valid file
handle.</td></tr> descriptor.</td></tr>
<tr><td valign=top>EMFILE</td> <td>The process's file table was full, or a <tr><td valign=top>EMFILE</td> <td>The process's file table was full, or a
process-specific limit on open files process-specific limit on open files
was reached.</td></tr> was reached.</td></tr>

View File

@ -57,7 +57,7 @@ struct stat *</tt><em>statbuf</em><tt>);</tt>
<h3>Description</h3> <h3>Description</h3>
<p> <p>
<tt>fstat</tt> retrieves status information about the file referred to <tt>fstat</tt> retrieves status information about the file referred to
by the file handle <em>fd</em> and stores it in the stat structure by the file descriptor <em>fd</em> and stores it in the stat structure
pointed to by <em>statbuf</em>. pointed to by <em>statbuf</em>.
</p> </p>

View File

@ -87,8 +87,8 @@ mentioned here.
<table width=90%> <table width=90%>
<tr><td width=5% rowspan=3>&nbsp;</td> <tr><td width=5% rowspan=3>&nbsp;</td>
<td width=10% valign=top>EBADF</td> <td width=10% valign=top>EBADF</td>
<td><em>fd</em> is not a valid file handle, or <td><em>fd</em> is not a valid file descriptor,
it is not open for writing.</td></tr> or it is not open for writing.</td></tr>
<tr><td valign=top>EIO</td> <td>A hard I/O error occurred.</td></tr> <tr><td valign=top>EIO</td> <td>A hard I/O error occurred.</td></tr>
<tr><td valign=top>EFAULT</td> <td><em>buf</em> points to an invalid <tr><td valign=top>EFAULT</td> <td><em>buf</em> points to an invalid
address.</td></tr> address.</td></tr>

View File

@ -57,7 +57,7 @@ size_t </tt><em>buflen</em><tt>);</tt>
<h3>Description</h3> <h3>Description</h3>
<p> <p>
<tt>getdirentry</tt> retrieves the next filename from a directory <tt>getdirentry</tt> retrieves the next filename from a directory
referred to by the file handle <em>filehandle</em>. The name is stored referred to by the file descriptor <em>fd</em>. The name is stored
in <em>buf</em>, an area of size <em>buflen</em>. The length of of the in <em>buf</em>, an area of size <em>buflen</em>. The length of of the
name actually found is returned. name actually found is returned.
</p> </p>

View File

@ -57,7 +57,7 @@ void *</tt><em>data</em><tt>);</tt>
<h3>Description</h3> <h3>Description</h3>
<p> <p>
<tt>ioctl</tt> performs an object-specific operation <em>code</em> on <tt>ioctl</tt> performs an object-specific operation <em>code</em> on
the object referred to by the file handle <em>fd</em>. The the object referred to by the file descriptor <em>fd</em>. The
<em>data</em> argument may point to supplemental data required or <em>data</em> argument may point to supplemental data required or
returned by the operation. The size of buffer required, if any, and returned by the operation. The size of buffer required, if any, and
other such matters are operation-specific. other such matters are operation-specific.

View File

@ -57,8 +57,8 @@ int </tt><em>whence</em><tt>);</tt>
<h3>Description</h3> <h3>Description</h3>
<p> <p>
<tt>lseek</tt> alters the current seek position of the file handle <tt>lseek</tt> alters the current seek position of the file handle
<em>filehandle</em>, seeking to a new position based on <em>pos</em> identified by file descriptor <em>fd</em>, seeking to a new position
and <em>whence</em>. based on <em>pos</em> and <em>whence</em>.
</p> </p>
<p> <p>
@ -122,7 +122,7 @@ mentioned here.
<tr><td width=5% rowspan=4>&nbsp;</td> <tr><td width=5% rowspan=4>&nbsp;</td>
<td width=10% valign=top>EBADF</td> <td width=10% valign=top>EBADF</td>
<td><em>fd</em> is not a valid file <td><em>fd</em> is not a valid file
handle.</td></tr> descriptor.</td></tr>
<tr><td valign=top>ESPIPE</td> <td><em>fd</em> refers to an object <tr><td valign=top>ESPIPE</td> <td><em>fd</em> refers to an object
which does not support seeking.</td></tr> which does not support seeking.</td></tr>
<tr><td valign=top>EINVAL</td> <td><em>whence</em> is invalid.</td></tr> <tr><td valign=top>EINVAL</td> <td><em>whence</em> is invalid.</td></tr>

View File

@ -99,12 +99,12 @@ course's assignments.)
</p> </p>
<p> <p>
<tt>open</tt> returns a file handle suitable for passing to <tt>open</tt> returns a file descriptor suitable for passing to
<A HREF=read.html>read</A>, <A HREF=read.html>read</A>,
<A HREF=write.html>write</A>, <A HREF=write.html>write</A>,
<A HREF=close.html>close</A>, <A HREF=close.html>close</A>,
etc. This file handle must be greater than or equal to zero. Note etc. This file descriptor must be greater than or equal to zero. Note
that file handles 0 (STDIN_FILENO), 1 (STDOUT_FILENO), and 2 that file descriptors 0 (STDIN_FILENO), 1 (STDOUT_FILENO), and 2
(STDERR_FILENO) are used in special ways and are typically assumed by (STDERR_FILENO) are used in special ways and are typically assumed by
user-level code to always be open. user-level code to always be open.
</p> </p>
@ -128,9 +128,9 @@ contain <tt>..</tt> is usually not quite atomic.
<h3>Return Values</h3> <h3>Return Values</h3>
<p> <p>
On success, <tt>open</tt> returns a nonnegative file handle. On error, On success, <tt>open</tt> returns a nonnegative file descriptor. On
-1 is returned, and <A HREF=errno.html>errno</A> is set according to error, -1 is returned, and <A HREF=errno.html>errno</A> is set
the error encountered. according to the error encountered.
</p> </p>
<h3>Errors</h3> <h3>Errors</h3>

View File

@ -51,7 +51,7 @@ Standard C Library (libc, -lc)
<br> <br>
<tt>ssize_t</tt><br> <tt>ssize_t</tt><br>
<tt>write(int </tt><em>fd</em><tt>, const void *</tt><em>buf</em><tt>, <tt>write(int </tt><em>fd</em><tt>, const void *</tt><em>buf</em><tt>,
size_t </tt><em>nbytes</em><tt>);</tt> size_t </tt><em>buflen</em><tt>);</tt>
</p> </p>
<h3>Description</h3> <h3>Description</h3>

View File

@ -87,15 +87,6 @@
# These build variables can be set explicitly for further control if # These build variables can be set explicitly for further control if
# desired, but should in general not need attention. # desired, but should in general not need attention.
# #
# (Tools.)
#
# PYTHON_INTERPRETER Location of Python interpreter.
# Default is "/usr/bin/env python".
#
# This may need to be changed on some platforms; but the configure
# script is supposed to take care of it for you. If that fails, or
# picks the wrong thing, please file a bug report.
#
# (Locations.) # (Locations.)
# #
# BUILDTOP Top of tree where .o files go. # BUILDTOP Top of tree where .o files go.
@ -479,7 +470,7 @@ MORECFLAGS+=-I$(INSTALLTOP)/include
LDFLAGS+=-nostdlib -L$(INSTALLTOP)/lib $(INSTALLTOP)/lib/crt0.o LDFLAGS+=-nostdlib -L$(INSTALLTOP)/lib $(INSTALLTOP)/lib/crt0.o
MORELIBS+=-lc MORELIBS+=-lc
LIBDEPS+=$(INSTALLTOP)/lib/crt0.o $(INSTALLTOP)/lib/libc.a LIBDEPS+=$(INSTALLTOP)/lib/crt0.o $(INSTALLTOP)/lib/libc.a
LIBS+=-ltest161
############################################################ ############################################################
# end. # end.

View File

@ -56,7 +56,7 @@ all-local: $(MYBUILDDIR) .WAIT $(MYBUILDDIR)/$(_LIB_)
install-staging-local: $(TOOLDIR)/hostlib .WAIT $(TOOLDIR)/hostlib/$(_LIB_) install-staging-local: $(TOOLDIR)/hostlib .WAIT $(TOOLDIR)/hostlib/$(_LIB_)
$(TOOLDIR)/hostlib/$(_LIB_): $(MYBUILDDIR)/$(_LIB_) $(TOOLDIR)/hostlib/$(_LIB_): $(MYBUILDDIR)/$(_LIB_)
rm -f $(.TARGET) rm -f $(.TARGET)
ln $(MYBUILDDIR)/$(_LIB_) $(.TARGET) || \ ln $(MYBUILDDIR)/$(_LIB_) $(.TARGET) >/dev/null 2>&1 || \
cp $(MYBUILDDIR)/$(_LIB_) $(.TARGET) cp $(MYBUILDDIR)/$(_LIB_) $(.TARGET)
install-local: install-local:

View File

@ -76,14 +76,14 @@ cleanhostprog:
install-staging-local: $(_INSTALLDIR_) .WAIT $(_INSTALLDIR_)/$(_PROG_) install-staging-local: $(_INSTALLDIR_) .WAIT $(_INSTALLDIR_)/$(_PROG_)
$(_INSTALLDIR_)/$(_PROG_): $(MYBUILDDIR)/$(_PROG_) $(_INSTALLDIR_)/$(_PROG_): $(MYBUILDDIR)/$(_PROG_)
rm -f $(.TARGET) rm -f $(.TARGET)
ln $(MYBUILDDIR)/$(_PROG_) $(.TARGET) || \ ln $(MYBUILDDIR)/$(_PROG_) $(.TARGET) >/dev/null 2>&1 || \
cp $(MYBUILDDIR)/$(_PROG_) $(.TARGET) cp $(MYBUILDDIR)/$(_PROG_) $(.TARGET)
.if defined(HOSTBINDIR) .if defined(HOSTBINDIR)
install-local: install-hostprog install-local: install-hostprog
install-hostprog: $(OSTREE)$(HOSTBINDIR) $(MYBUILDDIR)/$(_PROG_) install-hostprog: $(OSTREE)$(HOSTBINDIR) $(MYBUILDDIR)/$(_PROG_)
rm -f $(OSTREE)$(HOSTBINDIR)/$(_PROG_) rm -f $(OSTREE)$(HOSTBINDIR)/$(_PROG_)
ln $(MYBUILDDIR)/$(_PROG_) $(OSTREE)$(HOSTBINDIR)/$(_PROG_) || \ ln $(MYBUILDDIR)/$(_PROG_) $(OSTREE)$(HOSTBINDIR)/$(_PROG_) >/dev/null 2>&1 || \
cp $(MYBUILDDIR)/$(_PROG_) $(OSTREE)$(HOSTBINDIR)/$(_PROG_) cp $(MYBUILDDIR)/$(_PROG_) $(OSTREE)$(HOSTBINDIR)/$(_PROG_)
.else .else
install-local: install-local:

View File

@ -163,6 +163,9 @@ includelinks:
clean: clean:
rm -f *.o *.a tags $(KERNEL) rm -f *.o *.a tags $(KERNEL)
rm -rf includelinks rm -rf includelinks
@ABSTOP=$$(readlink -f $(TOP))
rm -f $(OSTREE)/.src
rm -f $(TOP)/.root
distclean cleandir: clean distclean cleandir: clean
rm -f .depend rm -f .depend
@ -188,6 +191,9 @@ install:
cp $(KERNEL) $(OSTREE)/$(KERNEL)-$(CONFNAME) cp $(KERNEL) $(OSTREE)/$(KERNEL)-$(CONFNAME)
-rm -f $(OSTREE)/$(KERNEL) -rm -f $(OSTREE)/$(KERNEL)
ln -s $(KERNEL)-$(CONFNAME) $(OSTREE)/$(KERNEL) ln -s $(KERNEL)-$(CONFNAME) $(OSTREE)/$(KERNEL)
@ABSTOP=$$(readlink -f $(TOP))
ln -Tsf $(ABSTOP) $(OSTREE)/.src
ln -Tsf $(OSTREE) $(ABSTOP)/.root
# #
# Run tags on all the sources and header files. This is probably not # Run tags on all the sources and header files. This is probably not

View File

@ -59,13 +59,13 @@ all-local: $(MYBUILDDIR) .WAIT $(MYBUILDDIR)/$(_LIB_)
install-staging-local: $(INSTALLTOP)$(LIBDIR) .WAIT $(INSTALLTOP)$(LIBDIR)/$(_LIB_) install-staging-local: $(INSTALLTOP)$(LIBDIR) .WAIT $(INSTALLTOP)$(LIBDIR)/$(_LIB_)
$(INSTALLTOP)$(LIBDIR)/$(_LIB_): $(MYBUILDDIR)/$(_LIB_) $(INSTALLTOP)$(LIBDIR)/$(_LIB_): $(MYBUILDDIR)/$(_LIB_)
rm -f $(.TARGET) rm -f $(.TARGET)
ln $(MYBUILDDIR)/$(_LIB_) $(.TARGET) || \ ln $(MYBUILDDIR)/$(_LIB_) $(.TARGET) >/dev/null 2>&1 || \
cp $(MYBUILDDIR)/$(_LIB_) $(.TARGET) cp $(MYBUILDDIR)/$(_LIB_) $(.TARGET)
install-local: $(OSTREE)$(LIBDIR) $(MYBUILDDIR)/$(_LIB_) install-local: $(OSTREE)$(LIBDIR) $(MYBUILDDIR)/$(_LIB_)
@echo "Warning: manually installing library without relinking anything" @echo "Warning: manually installing library without relinking anything"
rm -f $(OSTREE)$(LIBDIR)/$(_LIB_) rm -f $(OSTREE)$(LIBDIR)/$(_LIB_)
ln $(MYBUILDDIR)/$(_LIB_) $(OSTREE)$(LIBDIR)/$(_LIB_) || \ ln $(MYBUILDDIR)/$(_LIB_) $(OSTREE)$(LIBDIR)/$(_LIB_) >/dev/null 2>&1 || \
cp $(MYBUILDDIR)/$(_LIB_) $(OSTREE)$(LIBDIR)/$(_LIB_) cp $(MYBUILDDIR)/$(_LIB_) $(OSTREE)$(LIBDIR)/$(_LIB_)
# Build the library. # Build the library.

View File

@ -44,14 +44,14 @@ install-staging-local: $(INSTALLTOP)$(MANDIR) .WAIT
install-staging-local: $(INSTALLTOP)$(MANDIR)/$(_F_) install-staging-local: $(INSTALLTOP)$(MANDIR)/$(_F_)
$(INSTALLTOP)$(MANDIR)/$(_F_): $(_F_) $(INSTALLTOP)$(MANDIR)/$(_F_): $(_F_)
rm -f $(.TARGET) rm -f $(.TARGET)
ln $(_F_) $(.TARGET) || cp $(_F_) $(.TARGET) ln $(_F_) $(.TARGET) >/dev/null 2>&1 || cp $(_F_) $(.TARGET)
.endfor .endfor
install-local: $(OSTREE)$(MANDIR) .WAIT installmanpages install-local: $(OSTREE)$(MANDIR) .WAIT installmanpages
installmanpages: installmanpages:
.for _F_ in $(MANFILES) .for _F_ in $(MANFILES)
rm -f $(OSTREE)$(MANDIR)/$(_F_) rm -f $(OSTREE)$(MANDIR)/$(_F_)
ln $(_F_) $(OSTREE)$(MANDIR)/$(_F_) || \ ln $(_F_) $(OSTREE)$(MANDIR)/$(_F_) >/dev/null 2>&1 || \
cp $(_F_) $(OSTREE)$(MANDIR)/$(_F_) cp $(_F_) $(OSTREE)$(MANDIR)/$(_F_)
.endfor .endfor

View File

@ -61,13 +61,13 @@ cleanprog:
install-staging-local: $(INSTALLTOP)$(BINDIR) .WAIT $(INSTALLTOP)$(BINDIR)/$(PROG) install-staging-local: $(INSTALLTOP)$(BINDIR) .WAIT $(INSTALLTOP)$(BINDIR)/$(PROG)
$(INSTALLTOP)$(BINDIR)/$(PROG): $(MYBUILDDIR)/$(PROG) $(INSTALLTOP)$(BINDIR)/$(PROG): $(MYBUILDDIR)/$(PROG)
rm -f $(.TARGET) rm -f $(.TARGET)
ln $(MYBUILDDIR)/$(PROG) $(.TARGET) || \ ln $(MYBUILDDIR)/$(PROG) $(.TARGET) >/dev/null 2>&1 || \
cp $(MYBUILDDIR)/$(PROG) $(.TARGET) cp $(MYBUILDDIR)/$(PROG) $(.TARGET)
install-local: install-prog install-local: install-prog
install-prog: $(OSTREE)$(BINDIR) $(MYBUILDDIR)/$(PROG) install-prog: $(OSTREE)$(BINDIR) $(MYBUILDDIR)/$(PROG)
rm -f $(OSTREE)$(BINDIR)/$(PROG) rm -f $(OSTREE)$(BINDIR)/$(PROG)
ln $(MYBUILDDIR)/$(PROG) $(OSTREE)$(BINDIR)/$(PROG) || \ ln $(MYBUILDDIR)/$(PROG) $(OSTREE)$(BINDIR)/$(PROG) >/dev/null 2>&1 || \
cp $(MYBUILDDIR)/$(PROG) $(OSTREE)$(BINDIR)/$(PROG) cp $(MYBUILDDIR)/$(PROG) $(OSTREE)$(BINDIR)/$(PROG)
# Link the program. # Link the program.

61
test161/commands/asst2.tc Normal file
View File

@ -0,0 +1,61 @@
templates:
- name: /testbin/consoletest
panics: maybe
output:
- text: "/testbin/consoletest: Able was i ere i saw elbA"
- name: /testbin/opentest
panics: maybe
- name: /testbin/readwritetest
panics: maybe
output:
- text: "/testbin/readwritetest: h4xa0rRq0Vgbc96tiYJ^!#nXzZSAKPO"
- name: /testbin/closetest
panics: maybe
- name: /testbin/fileonlytest
panics: maybe
- name: /testbin/redirect
- name: /testbin/sparsefile
panics: maybe
- name: /testbin/badcall
- name: faulter
- name: /testbin/forkbomb
timesout: yes
timeout: 15.0
- name: kmalloc
output:
- text: "kmalloc: out of memory"
- name: /testbin/crash
- name: /testbin/forktest
- name: /testbin/randcall
- name: /testbin/shelltest
output:
- text: "/testbin/shelltest: line-1: Able was i ere i saw elbA"
- text: "/testbin/shelltest: line-2: Able was i ere i saw elbA"
- text: "/testbin/shelltest: line-3: Able was i ere i saw elbA"
- name: /testbin/argtest
input:
- "{{randString 1 2}}"
- "{{randString 4 5}}"
- "{{randString 9 10}}"
- "{{randString 10 11}}"
- "{{randString 11 12}}"
- "{{randString 12 13}}"
- "{{randString 13 14}}"
- "{{randString 200 513}}"
output:
- text: "/testbin/argtest: argc: {{add 1 .ArgLen}}"
- text: "/testbin/argtest: /testbin/argtest"
- text: "{{range $index, $element := .Args}}/testbin/argtest: {{$element}}\n{{end}}"
- text: "/testbin/argtest: [NULL]"
- name: /testbin/bigexec
- name: /testbin/factorial
input:
- "{{randInt 6 10}}"
output:
- text: "/testbin/factorial: {{$n:= index .Args 0 | atoi}}{{factorial $n}}\n"
- name: /testbin/add
input:
- "{{randInt 2 1000}}"
- "{{randInt 2 4000}}"
output:
- text: "/testbin/add: {{$x:= index .Args 0 | atoi}}{{$y := index .Args 1 | atoi}}{{add $x $y}}"

View File

@ -0,0 +1,6 @@
templates:
- name: km1
- name: km2
- name: km3
- name: km4
- name: km5

34
test161/commands/misc.tc Normal file
View File

@ -0,0 +1,34 @@
# These commands expect no output, but must not panic
templates:
- name: tt1
output:
- text: ""
- name: tt2
output:
- text: ""
- name: tt3
output:
- text: ""
- name: khu
output:
- text: ""
- name: q
output:
- text: ""
- name: s
output:
- text: ""
- name: boot
output:
- text:
- name: exit
output:
- text: ""

38
test161/commands/sync.tc Normal file
View File

@ -0,0 +1,38 @@
templates:
- name: sem1
- name: lt1
- name: lt2
panics: yes
output:
- text: "lt2: Should panic..."
- name: lt3
panics: yes
output:
- text: "lt3: Should panic..."
- name: cvt1
- name: cvt2
- name: cvt3
panics: yes
output:
- text: "cvt3: Should panic..."
- name: cvt4
panics: yes
output:
- text: "cvt4: Should panic..."
- name: cvt5
- name: rwt1
- name: rwt2
- name: rwt3
panics: yes
output:
- text: "rwt3: Should panic..."
- name: rwt4
panics: yes
output:
- text: "rwt4: Should panic..."
- name: rwt5
panics: yes
output:
- text: "rwt5: Should panic..."
- name: sp1
- name: sp2

52
test161/commands/vm.tc Normal file
View File

@ -0,0 +1,52 @@
templates:
#Single tests - everything has the default output
- name: /testbin/bigfork
- name: /testbin/ctest
- name: /testbin/huge
- name: /testbin/matmult
- name: /testbin/palin
- name: /testbin/parallelvm
- name: /testbin/sbrktest
- name: /testbin/sort
- name: /testbin/stacktest
- name: /testbin/zero
#Triples
- name: /testbin/triplehuge
output:
- {text: /testbin/huge, external: true, trusted: true}
- {text: /testbin/huge, external: true, trusted: true}
- {text: /testbin/huge, external: true, trusted: true}
- name: /testbin/triplemat
output:
- {text: /testbin/matmult, external: true, trusted: true}
- {text: /testbin/matmult, external: true, trusted: true}
- {text: /testbin/matmult, external: true, trusted: true}
- name: /testbin/triplesort
output:
- {text: /testbin/sort, external: true, trusted: true}
- {text: /testbin/sort, external: true, trusted: true}
- {text: /testbin/sort, external: true, trusted: true}
#Quints
- name: /testbin/quinthuge
output:
- {text: /testbin/huge, external: true, trusted: true}
- {text: /testbin/huge, external: true, trusted: true}
- {text: /testbin/huge, external: true, trusted: true}
- {text: /testbin/huge, external: true, trusted: true}
- {text: /testbin/huge, external: true, trusted: true}
- name: /testbin/quintmat
output:
- {text: /testbin/matmult, external: true, trusted: true}
- {text: /testbin/matmult, external: true, trusted: true}
- {text: /testbin/matmult, external: true, trusted: true}
- {text: /testbin/matmult, external: true, trusted: true}
- {text: /testbin/matmult, external: true, trusted: true}
- name: /testbin/quintsort
output:
- {text: /testbin/sort, external: true, trusted: true}
- {text: /testbin/sort, external: true, trusted: true}
- {text: /testbin/sort, external: true, trusted: true}
- {text: /testbin/sort, external: true, trusted: true}
- {text: /testbin/sort, external: true, trusted: true}

70
test161/tags/all.td Normal file
View File

@ -0,0 +1,70 @@
tags:
- name: badcall
desc: "All badcall tests for the various system calls"
- name: boot
desc: "Tests that check if your kernel can boot"
- name: console
desc: "Tests that check if you have a working userspace console"
- name: coremap
desc: "All coremap-related tests"
- name: crash
desc: "Tests that attempt to crash your kernel"
- name: cvs
desc: "Condition variable tests"
- name: filesyscalls
desc: "Filesystem syscall tests, e.g. read, write, open, close, etc."
- name: kleaks
desc: "Synch tests that also check for memory leaks"
- name: locks
desc: "Kernel lock tests"
- name: not-dumbvm
desc: "Tests that verify your coremap is not using dumbvm"
- name: not-dumbvm-vm
desc: "Tests that verify your VM system is not using dumbvm"
- name: proc
desc: "Misc. process system call tests"
- name: procsyscalls
desc: "Tests that test process system calls, e.g. fork, exec, waitpid"
- name: rwlocks
desc: "Reader/writer lock tests"
- name: sbrk
desc: "sbrk tests"
- name: semaphores
desc: "Kernel semaphore tests"
- name: shell
desc: "Tests that check for a working userspace shell"
- name: stability
desc: "System call stability tests that stress test and check various error conditions"
- name: stability-vm
desc: "VM stress testing"
- name: swap
desc: "Tests that stress test your VM swapping implementation"
- name: swap-basic
desc: "Less stressful swap tests to ensure a swapping implementation"
- name: synch
desc: "All sychronization primitive tests"
- name: synchprobs
desc: "Synchronization problems that test your ablility to use synch primitives"
- name: sys_close
desc: "close() syscall tests"
- name: sys_dup2
desc: "dup2() syscall tests"
- name: sys_exec
desc: "exec() syscall tests"
- name: sys_fork
desc: "fork() syscall tests"
- name: sys_lseek
desc: "lseek() syscall tests"
- name: sys_open
desc: "open() syscall tests"
- name: sys_read
desc: "read() syscall tests"
- name: sys_write
desc: "write() syscall tests"
- name: syscalls
desc: "Tests that check the basic functionality of your system call implementations"
- name: threads
desc: "Kernel thread tests"
- name: vm
desc: "All non-swapping VM tests"

40
test161/targets/asst1.tt Normal file
View File

@ -0,0 +1,40 @@
name: asst1
print_name: ASST1
description: >
In this assignment you will implement new synchronization
primitives for OS/161 and use them to solve several synchronization problems.
version: 1
points: 50
type: asst
kconfig: ASST1
tests:
- id: synch/lt1.t
points: 8
- id: synch/lt2.t
points: 1
- id: synch/lt3.t
points: 1
- id: synch/cvt1.t
points: 4
- id: synch/cvt2.t
points: 3
- id: synch/cvt3.t
points: 1
- id: synch/cvt4.t
points: 1
- id: synch/cvt5.t
points: 1
- id: synch/rwt1.t
points: 5
- id: synch/rwt2.t
points: 2
- id: synch/rwt3.t
points: 1
- id: synch/rwt4.t
points: 1
- id: synch/rwt5.t
points: 1
- id: synchprobs/sp1.t
points: 10
- id: synchprobs/sp2.t
points: 10

View File

@ -0,0 +1,51 @@
name: asst2-single
print_name: "ASST2 (Single)"
description: >
In this assignment you will add process and system call support to your
OS/161 kernel. This is the single-submitter version for students that are
working alone and were provided the file system system calls.
leaderboard: false
version: 1
points: 80
type: asst
kconfig: ASST2
userland: true
tests:
# Process system call tests (50 points)
- id: syscalls/forktest.t
points: 15
- id: syscalls/shell.t
points: 10
- id: syscalls/argtest.t
points: 5
- id: syscalls/bigexec.t
points: 10
- id: syscalls/factorial.t
points: 5
- id: syscalls/add.t
points: 5
# Stability tests (30 points)
# Bad calls (5 points)
- id: stability/badcall/badcall-execv.t
points: 3
- id: stability/badcall/badcall-waitpid.t
points: 2
# crash
- id: stability/crash/crash-allS.t
points: 3
# randcall
- id: stability/randcall.t
points: 2
commands:
- id: /testbin/randcall
args:
- "-f"
- "-c 100"
- "-r 421"
- "2"
# forkbomb
- id: stability/forkbomb.t
points: 10
# forktest
- id: stability/forktest-stability.t
points: 10

View File

@ -0,0 +1,15 @@
name: asst2.1
print_name: ASST2.1
description: >
In this part of the assignment, you will add console support.
version: 1
points: 20
type: asst
kconfig: ASST2
userland: true
meta_name: asst2
leaderboard: false
tests:
# Make sure the console works (10 points)
- id: syscalls/consoletest.t
points: 20

View File

@ -0,0 +1,76 @@
name: asst2.2
print_name: ASST2.1
description: >
In this assignment you will add process and system call support to your
OS/161 kernel.
version: 1
points: 130
type: asst
kconfig: ASST2
userland: true
meta_name: asst2
leaderboard: false
tests:
# File system system calls (47 points)
- id: syscalls/opentest.t
points: 5
- id: syscalls/closetest.t
points: 5
- id: syscalls/readwritetest.t
points: 10
- id: syscalls/fileonlytest.t
points: 12
- id: syscalls/redirect.t
points: 10
- id: syscalls/sparsefile.t
points: 5
# Process system call tests (47 points)
- id: syscalls/forktest.t
points: 15
- id: syscalls/shell.t
points: 10
- id: syscalls/argtest.t
points: 5
- id: syscalls/bigexec.t
points: 8
- id: syscalls/factorial.t
points: 5
- id: syscalls/add.t
points: 4
# Stability tests (36 points)
# Bad calls (8 points)
- id: stability/badcall/badcall-open.t
points: 1
- id: stability/badcall/badcall-close.t
points: 1
- id: stability/badcall/badcall-read.t
points: 1
- id: stability/badcall/badcall-write.t
points: 1
- id: stability/badcall/badcall-lseek.t
points: 1
- id: stability/badcall/badcall-dup2.t
points: 1
- id: stability/badcall/badcall-execv.t
points: 1
- id: stability/badcall/badcall-waitpid.t
points: 1
# crash
- id: stability/crash/crash-allS.t
points: 4
# randcall
- id: stability/randcall.t
points: 4
commands:
- id: /testbin/randcall
args:
- "-f"
- "-c 100"
- "-r 421"
- "2"
# forkbomb
- id: stability/forkbomb.t
points: 10
# forktest
- id: stability/forktest-stability.t
points: 10

12
test161/targets/asst2.tt Normal file
View File

@ -0,0 +1,12 @@
name: asst2
print_name: ASST2
description: >
In this assignment you will add process and system call support to your
OS/161 kernel.
version: 2
points: 150
type: asst
kconfig: ASST2
userland: true
is_meta_target: true
sub_target_names: ["asst2.1", "asst2.2"]

View File

@ -0,0 +1,31 @@
name: asst3.1
print_name: ASST3.1
description: >
In this part of ASST3, you will begin writing your own VM by adding your
coremap and page allocator.
version: 1
points: 60
type: asst
kconfig: ASST3
userland: true
meta_name: asst3
leaderboard: false
tests:
- id: coremap/not-dumbvm.t
points: 15
- id: coremap/km1.t
points: 5
mem_leak_points: 2
- id: coremap/km2.t
points: 5
mem_leak_points: 2
- id: coremap/km3.t
points: 5
mem_leak_points: 2
- id: coremap/km4.t
points: 15
mem_leak_points: 2
- id: coremap/coremap-loose.t
points: 5
- id: coremap/coremap-tight.t
points: 10

View File

@ -0,0 +1,55 @@
name: asst3.2
print_name: ASST3.2
description: >
In this part of ASST3, you will add virtual address spaces to your VM.
version: 1
points: 120
type: asst
kconfig: ASST3
userland: true
meta_name: asst3
leaderboard: false
tests:
# Basic VM (30 points)
- id: vm/not-dumbvm-vm.t
points: 5
- id: vm/sort.t
points: 5
- id: vm/palin.t
points: 5
- id: vm/matmult.t
mem_leak_points: 2
points: 5
- id: vm/ctest.t
points: 5
- id: vm/stacktest.t
points: 5
# Concurrent VM (50 points)
- id: vm/bigfork.t
points: 10
mem_leak_points: 2
- id: vm/parallelvm.t
points: 10
- id: vm/quintsort.t
points: 10
- id: vm/quintmat.t
points: 10
- id: vm/quinthuge.t
points: 10
# Heap (10 points)
- id: vm/sbrktest.t
points: 8
mem_leak_points: 2
- id: vm/sbrk-badcall.t
points: 2
# Stress tests/misc (30 points)
- id: vm/zero.t
points: 5
- id: vm/stability/vm-stability.t
points: 20
mem_leak_points: 2
- id: vm/stability/forkbomb.t
points: 5

View File

@ -0,0 +1,37 @@
name: asst3.3
print_name: ASST3.3
description: >
In this part of ASST3, you will add swapping to your VM.
version: 1
points: 120
type: asst
kconfig: ASST3
userland: true
meta_name: asst3
leaderboard: false
tests:
# Basic Swapping (40 points)
- id: vm/swap/sort.t
mem_leak_points: 2
points: 20
- id: vm/swap/matmult.t
mem_leak_points: 2
points: 20
# Concurrent Swapping (80 points)
- id: vm/swap/bigfork.t
points: 10
- id: vm/swap/bigfork-32.t
points: 15
mem_leak_points: 2
- id: vm/swap/parallelvm.t
points: 10
- id: vm/swap/parallelvm-32.t
points: 15
- id: vm/swap/quintsort.t
points: 10
- id: vm/swap/quintmat.t
points: 10
- id: vm/swap/quinthuge.t
points: 10
mem_leak_points: 2

12
test161/targets/asst3.tt Normal file
View File

@ -0,0 +1,12 @@
name: asst3
print_name: ASST3
description: >
In this assignment you will add support for virtual memory to your OS/161
kernel.
version: 4
points: 300
type: asst
kconfig: ASST3
userland: true
is_meta_target: true
sub_target_names: ["asst3.1", "asst3.2", "asst3.3"]

9
test161/tests/boot.t Normal file
View File

@ -0,0 +1,9 @@
---
name: Kernel Boot
description:
Tests whether your kernel will boot.
tags: [boot]
sys161:
cpus: 2
---
q

View File

@ -0,0 +1,11 @@
---
name: "Coremap Test (Loose Bounds)"
description: >
Allocates and frees all physical memory multiple times checking
that the amount allocated is within a reasonable bound.
tags: [coremap]
depends: [not-dumbvm.t]
sys161:
ram: 4M
---
| km5 --avail 32 --kernel 125

View File

@ -0,0 +1,11 @@
---
name: "Coremap Test (Tight Bounds)"
description: >
Allocates and frees all physical memory multiple times checking
that the amount allocated is within a reasonable bound.
tags: [coremap]
depends: [not-dumbvm.t]
sys161:
ram: 4M
---
| km5 --avail 20 --kernel 105

View File

@ -0,0 +1,9 @@
---
name: "Basic kmalloc Test"
description: >
Tests the kernel subpage allocator by allocating a large number of objects
and freeing them somewhat later.
tags: [coremap]
depends: [not-dumbvm.t]
---
| km1

View File

@ -0,0 +1,8 @@
---
name: "kmalloc Stress Test"
description: >
Similar to km1 but uses multiple concurrent threads.
tags: [coremap]
depends: [not-dumbvm.t]
---
| km2

View File

@ -0,0 +1,9 @@
---
name: "Large kmalloc Test"
description: >
Stresses the subpage allocator by allocating and freeing a large number of
objects of various sizes.
tags: [coremap]
depends: [not-dumbvm.t]
---
| km3 5000

View File

@ -0,0 +1,9 @@
---
name: "Multipage allococation Test"
description: >
Allocates and frees between 1 and 5 pages a number of times by a number of
concurrent threads.
tags: [coremap]
depends: [not-dumbvm.t]
---
| km4

View File

@ -0,0 +1,9 @@
---
name: "Smarter VM"
description:
Test whether you are using dumbvm by allocating and freeing all physical
memory multiple times.
tags: [coremap, not-dumbvm]
depends: [boot]
---
| km5

View File

@ -0,0 +1,10 @@
---
name: "Bad Close"
description: >
Stability test for sys_close.
tags: [badcall,stability]
depends: [shell]
sys161:
ram: 2M
---
$ /testbin/badcall f

View File

@ -0,0 +1,10 @@
---
name: "Bad Dup"
description:
Stability test for sys_dup2.
tags: [badcall,stability]
depends: [shell]
sys161:
ram: 2M
---
$ /testbin/badcall w

View File

@ -0,0 +1,10 @@
---
name: "Bad Exec"
description:
Stability test for sys_exec.
tags: [badcall,stability]
depends: [shell]
sys161:
ram: 2M
---
$ /testbin/badcall a

View File

@ -0,0 +1,10 @@
---
name: "Bad Seek"
description:
Stability test for sys_lseek.
tags: [badcall,stability]
depends: [shell]
sys161:
ram: 2M
---
$ /testbin/badcall j

View File

@ -0,0 +1,10 @@
---
name: "Bad Open"
description:
Stability test for sys_open.
tags: [badcall,stability]
depends: [shell]
sys161:
ram: 2M
---
$ /testbin/badcall c

View File

@ -0,0 +1,10 @@
---
name: "Bad Read"
description:
Stability test for sys_read.
tags: [badcall,stability]
depends: [shell]
sys161:
ram: 2M
---
$ /testbin/badcall d

View File

@ -0,0 +1,10 @@
---
name: "Bad Wait"
description:
Stability test for sys_wait.
tags: [badcall,stability]
depends: [shell]
sys161:
ram: 2M
---
$ /testbin/badcall b

View File

@ -0,0 +1,10 @@
---
name: "Bad Write"
description:
Stability test for sys_write.
tags: [badcall,stability]
depends: [shell]
sys161:
ram: 2M
---
$ /testbin/badcall e

View File

@ -0,0 +1,11 @@
---
name: "Crash"
description: >
Tests whether your system correctly handles a variety of bad process
behavior.
tags: [crash,stability]
depends: [shell,sys_fork]
sys161:
ram: 4M
---
$ /testbin/crash *

View File

@ -0,0 +1,10 @@
---
name: "Crash-template Test"
description:
Tests whether your system correctly handles a variety of
illegal attempts from userspace processes to access resources that
do not belong to it. Please see userland/testbin/crash for more info.
tags: [stability, crash]
depends: [console]
---
p /extratests/crash-template

View File

@ -0,0 +1,54 @@
import os,sys
import subprocess
RANGE=[chr(i) for i in range(ord('a'), ord('p'))]
# First create crash-a/b/c.t files
template = open('crash-template').read()
def add_sys161_opts(content, sys161_opts):
content = content.split('\n')
idx = content[1:].index('---') + 1
content.insert(idx, sys161_opts.strip())
content = '\n'.join(content)
return content
def create_crash(char, sys161_opts=None):
content = template.replace('-template', '-%s' % char)
content = content.replace(', crash]', ', crash-fork]')
content = content.replace('[console]', '[console, /asst2/process/forktest.t]')
if sys161_opts:
content = add_sys161_opts(content, sys161_opts)
with open('crash-%s.t' % char, 'w') as f:
f.write(content)
# now do the F version
content = template.replace('-template', '-%sF' % char)
if sys161_opts:
content = add_sys161_opts(content, sys161_opts)
with open('crash-%sF.t' % char, 'w') as f:
f.write(content)
for char in RANGE:
create_crash(char)
# Now do the 'all'
sys161_opts = \
'''
sys161:
ram: 2M
'''
create_crash('all', sys161_opts)
#p = subprocess.Popen('ls *.t', shell=True, stdout=subprocess.PIPE)
#stdout, stderr = p.communicate()
#files = stdout.strip().split('\n')
#
#for f in files:
# name, ext = os.path.splitext(f)
#
# new_file = name + 'F' + ext
# letters = name[name.index('-'):]
# subprocess.check_call('cp %s %s' % (f, new_file), shell=True)
# subprocess.check_call('''sed -i 's/%s/%sF/g' %s''' % (letters, letters, new_file), shell=True)

View File

@ -0,0 +1,10 @@
---
name: "Faulter Test"
description:
Tests whether kill_curthread is implemented correctly.
Attempts to access an invalid memory address expecting the kernel
to gracefully kill the current process instead of panicking.
tags: [proc]
depends: [console]
---
p /testbin/faulter

View File

@ -0,0 +1,17 @@
---
name: "Calm Like a Fork Bomb"
tags: [stability]
depends: [console,sys_fork]
sys161:
ram: 2M
monitor:
progresstimeout: 40.0
user:
enablemin: true
min: 0.001
max: 1.0
kernel:
enablemin: true
min: 0.01
---
p /testbin/forkbomb

View File

@ -0,0 +1,14 @@
---
name: "Fork Stability Test"
description:
Runs forktest 5 times to check for synchronization issues.
tags: [stability]
depends: [shell]
sys161:
ram: 16M
---
$ /testbin/forktest
$ /testbin/forktest
$ /testbin/forktest
$ /testbin/forktest
$ /testbin/forktest

View File

@ -0,0 +1,10 @@
---
name: "Randcall Test"
description: >
Invokes system calls with random arguments.
tags: [stability]
depends: [shell]
sys161:
ram: 16M
---
$ /testbin/randcall -f -c 100 -r 421 2

Some files were not shown because too many files have changed in this diff Show More